|
2 | 2 | <span v-if="isText(token)"> |
3 | 3 | <span v-if="!token.tokens || token.tokens.length === 0"> |
4 | 4 | <span v-if="isComponent(token)"> |
5 | | - <component :is="parsedComponent(token).is" v-if="!last" :text="parsedComponent(token).text" /> |
| 5 | + <!-- <component :is="parsedComponent(token).is" v-if="!last" :text="parsedComponent(token).text" /> --> |
| 6 | + <component :is="getComponent(parsedComponent(token).is)" v-if="!last" :text="parsedComponent(token).text" /> |
6 | 7 | </span> |
7 | 8 | <span v-else-if="containsComponent(token)"> |
8 | 9 | <md-token-renderer v-for="(subTok, j) in splitTextToken(token)" :key="j" :token="subTok" /> |
|
92 | 93 |
|
93 | 94 | <span v-else-if="token.type === 'html'" v-html="token.raw"></span> |
94 | 95 |
|
95 | | - <highlightjs v-else-if="token.type === 'code'" class="hljs_render pa-2" :language="token.lang" :code="token.text" /> |
| 96 | + <code-block-renderer v-else-if="token.type === 'code'" :code="token.text" :language="token.lang" /> |
| 97 | + <!-- <highlightjs v-else-if="token.type === 'code'" class="hljs_render pa-2" :language="token.lang" :code="token.text" /> --> |
96 | 98 |
|
97 | 99 | <code v-else-if="token.type === 'codespan'" class="codespan" v-html="token.text"></code> |
98 | 100 |
|
|
107 | 109 | </em> |
108 | 110 | </template> |
109 | 111 |
|
110 | | -<script lang="ts"> |
111 | | -import RadioMultipleChoice from '@/base-course/Components/RadioMultipleChoice.vue'; |
| 112 | +<script setup lang="ts"> |
112 | 113 | import { |
113 | | - containsComponent, |
114 | | - isComponent, |
115 | | - splitParagraphToken, |
116 | | - splitTextToken, |
| 114 | + containsComponent as _containsComponent, |
| 115 | + isComponent as _isComponent, |
| 116 | + splitParagraphToken as _splitParagraphToken, |
| 117 | + splitTextToken as _splitTextToken, |
117 | 118 | TokenOrComponent, |
118 | 119 | } from '@/courses/default/questions/fillIn'; |
| 120 | +import CodeBlockRenderer from './CodeBlockRenderer.vue'; |
119 | 121 | import FillInInput from '@/courses/default/questions/fillIn/fillInInput.vue'; |
120 | | -import { marked } from 'marked'; |
121 | | -import { defineComponent } from 'vue'; |
| 122 | +import { MarkedToken, Tokens } from 'marked'; |
| 123 | +import { markRaw } from 'vue'; |
| 124 | +import { PropType } from 'vue'; |
| 125 | +
|
| 126 | +// Register components to be used in the template |
| 127 | +// In Vue 3 with <script setup>, components used via :is must be explicitly registered |
| 128 | +const components = { |
| 129 | + fillIn: markRaw(FillInInput), |
| 130 | + // Add other dynamic components here |
| 131 | +}; |
| 132 | +
|
| 133 | +// Define component props |
| 134 | +defineProps({ |
| 135 | + token: { |
| 136 | + type: Object as PropType<TokenOrComponent>, |
| 137 | + required: true, |
| 138 | + }, |
| 139 | + last: { |
| 140 | + type: Boolean, |
| 141 | + required: false, |
| 142 | + default: false, |
| 143 | + }, |
| 144 | +}); |
122 | 145 |
|
123 | | -// import hljs from 'highlight.js'; |
124 | | -// import 'highlight.js/styles/atelier-seaside-light.css'; // Move CSS import here |
125 | | -// import 'highlight.js/styles/atelier-seaside-light.css'; |
126 | | -import hljsVuePlugin from '@highlightjs/vue-plugin'; |
| 146 | +// Methods |
| 147 | +function isComponent(token: MarkedToken): boolean { |
| 148 | + const result = _isComponent(token); |
| 149 | + return result; |
| 150 | +} |
127 | 151 |
|
128 | | -export default defineComponent({ |
129 | | - name: 'MdTokenRenderer', |
| 152 | +function containsComponent(token: MarkedToken): boolean { |
| 153 | + const result = _containsComponent(token); |
| 154 | + return result; |
| 155 | +} |
130 | 156 |
|
131 | | - components: { |
132 | | - fillIn: FillInInput, |
133 | | - RadioMultipleChoice, |
134 | | - highlightjs: hljsVuePlugin.component, |
135 | | - }, |
| 157 | +function splitTextToken(token: MarkedToken): Tokens.Text[] { |
| 158 | + return _splitTextToken(token); |
| 159 | +} |
136 | 160 |
|
137 | | - props: { |
138 | | - token: { |
139 | | - type: Object as () => marked.Token | TokenOrComponent, |
140 | | - required: true, |
141 | | - }, |
142 | | - last: { |
143 | | - type: Boolean, |
144 | | - required: false, |
145 | | - default: false, |
146 | | - }, |
147 | | - }, |
| 161 | +function splitParagraphToken(token: Tokens.Paragraph): TokenOrComponent[] { |
| 162 | + return _splitParagraphToken(token); |
| 163 | +} |
148 | 164 |
|
149 | | - methods: { |
150 | | - isComponent(token: marked.Token): boolean { |
151 | | - return isComponent(token); |
152 | | - }, |
153 | | -
|
154 | | - containsComponent(token: marked.Token): boolean { |
155 | | - return containsComponent(token); |
156 | | - }, |
157 | | -
|
158 | | - splitTextToken(token: marked.Tokens.Text): marked.Token[] { |
159 | | - return splitTextToken(token); |
160 | | - }, |
161 | | -
|
162 | | - splitParagraphToken(token: marked.Tokens.Paragraph): TokenOrComponent[] { |
163 | | - return splitParagraphToken(token); |
164 | | - }, |
165 | | -
|
166 | | - parsedComponent( |
167 | | - token: marked.Tokens.Text |
168 | | - ): { |
169 | | - is: string; |
170 | | - text: string; |
171 | | - } { |
172 | | - // [ ] switching on component types & loading custom component |
173 | | - // |
174 | | - // sketch: |
175 | | - // const demoustached = token.text.slice(2, token.text.length - 2); |
176 | | - // const firstToken = demoustached.split(' ')[0]; |
177 | | - // if (firstToken.charAt(firstToken.length - 1) == '>') { |
178 | | - // return { |
179 | | - // is: firstToken.slice(0, firstToken.length - 1), |
180 | | - // text: demoustached.slice(firstToken.length + 1, demoustached.length), |
181 | | - // }; |
182 | | - // } |
183 | | -
|
184 | | - return { |
185 | | - is: 'fillIn', |
186 | | - text: token.text, |
187 | | - }; |
188 | | - }, |
189 | | -
|
190 | | - decodeBasicEntities(text: string): string { |
191 | | - return text |
192 | | - .replace(/'/g, "'") |
193 | | - .replace(/"/g, '"') |
194 | | - .replace(/&/g, '&') |
195 | | - .replace(/</g, '<') |
196 | | - .replace(/>/g, '>'); |
197 | | - }, |
198 | | -
|
199 | | - isText(tok: TokenOrComponent): tok is marked.Tokens.Text { |
200 | | - return (tok as marked.Tokens.Tag).inLink === undefined && tok.type === 'text'; |
201 | | - }, |
202 | | - }, |
| 165 | +function parsedComponent(token: MarkedToken): { |
| 166 | + is: string; |
| 167 | + text: string; |
| 168 | +} { |
| 169 | + // [ ] switching on component types & loading custom component |
| 170 | + // |
| 171 | + // sketch: |
| 172 | + // const demoustached = token.text.slice(2, token.text.length - 2); |
| 173 | + // const firstToken = demoustached.split(' ')[0]; |
| 174 | + // if (firstToken.charAt(firstToken.length - 1) == '>') { |
| 175 | + // return { |
| 176 | + // is: firstToken.slice(0, firstToken.length - 1), |
| 177 | + // text: demoustached.slice(firstToken.length + 1, demoustached.length), |
| 178 | + // }; |
| 179 | + // } |
| 180 | +
|
| 181 | + let text = ''; |
| 182 | + if ('text' in token && typeof token.text === 'string') { |
| 183 | + text = token.text; |
| 184 | + } else if ('raw' in token && typeof token.raw === 'string') { |
| 185 | + text = token.raw; |
| 186 | + } |
| 187 | +
|
| 188 | + // This now returns a component from our registered components object |
| 189 | + return { |
| 190 | + is: 'fillIn', // This should match a key in the components object |
| 191 | + text, |
| 192 | + }; |
| 193 | +} |
| 194 | +
|
| 195 | +function getComponent(componentName: string) { |
| 196 | + // Return the component instance from our components object |
| 197 | + return components[componentName as keyof typeof components]; |
| 198 | +} |
| 199 | +
|
| 200 | +function decodeBasicEntities(text: string): string { |
| 201 | + return text |
| 202 | + .replace(/'/g, "'") |
| 203 | + .replace(/"/g, '"') |
| 204 | + .replace(/&/g, '&') |
| 205 | + .replace(/</g, '<') |
| 206 | + .replace(/>/g, '>'); |
| 207 | +} |
| 208 | +
|
| 209 | +function isText(tok: TokenOrComponent): boolean { |
| 210 | + return (tok as any).inLink === undefined && tok.type === 'text'; |
| 211 | +} |
| 212 | +
|
| 213 | +// Make these functions and objects available to the template |
| 214 | +defineExpose({ |
| 215 | + isComponent, |
| 216 | + containsComponent, |
| 217 | + splitTextToken, |
| 218 | + splitParagraphToken, |
| 219 | + parsedComponent, |
| 220 | + decodeBasicEntities, |
| 221 | + isText, |
| 222 | + components, |
| 223 | + getComponent, |
203 | 224 | }); |
204 | 225 | </script> |
205 | 226 |
|
206 | 227 | <style lang="css" scoped> |
207 | | -/* @import './../../../node_modules/highlight.js/styles/atelier-seaside-light.css'; */ |
208 | | -/* @import './../../../node_modules/highlight.js/styles/stackoverflow-light.css'; */ |
209 | | -/* @import './../../../node_modules/highlight.js/styles/xt256.css'; */ |
210 | | -/* @import './../../../node_modules/highlight.js/styles/zenburn.css'; */ |
211 | | -/* @import './../../../node_modules/highlight.js/styles/tomorrow.css'; */ |
212 | | -/* @import './../../../node_modules/highlight.js/styles/lioshi.css'; */ |
213 | | -/* @import './../../../node_modules/highlight.js/styles/rainbow.css'; */ |
214 | | -/* @import './../../../node_modules/highlight.js/styles/monokai-sublime.css'; */ |
215 | | -
|
216 | 228 | blockquote { |
217 | 229 | border-left: 3px teal solid; |
218 | 230 | padding-left: 8px; |
|
0 commit comments