@@ -25,16 +25,24 @@ This component displays rich text with optional autolink or [Markdown support](h
2525export default {
2626 data() {
2727 return {
28- text: `## Hello everyone 🎉
29- The file {file} was added by {username}. Visit https://nextcloud.com to check it!
28+ text: `3. one
29+ 4. three
30+ 5. five
31+ 6. eleven
3032
31- Some examples for markdown syntax:
32- 1. **bold text**
33- 2. _italic text_
34- 3. example of \`inline code\`
33+ fsdf
3534
36- > blockquote example
37- `,
35+ 2. one
36+ 4. three
37+ 7. five
38+ 11. eleven
39+
40+ afsfd
41+
42+ 3. one
43+ 4. three
44+ 5. five
45+ 6. eleven`,
3846 autolink: true,
3947 useMarkdown: true,
4048 args: {
@@ -62,243 +70,6 @@ textarea {
6270</style>
6371```
6472
65- ### Flavored Markdown
66-
67- This component can support [Github Flavored Markdown](https://github.github.com/gfm/).
68- It adds such elements, as tables, task lists, strikethrough, and supports code syntax highlighting and autolinks by default
69-
70- It is also possible to make a rendered content interactive and listen for events
71-
72- ```vue
73- <template>
74- <div>
75- <textarea v-model="text" />
76-
77- <NcRichText :text="text"
78- :use-extended-markdown="true"
79- :interactive="true"
80- @interact-todo="handleInteraction"/>
81- </div>
82- </template>
83- <script>
84- export default {
85- data() {
86- return {
87- text: `## Try flavored markdown right now!
88-
89- ~~strikethrough~~
90-
91- - [ ] task to be done
92- - [x] task completed
93-
94- Table header | Column A | Column B
95- -- | -- | --
96- Table row | value A | value B
97-
98- ---
99-
100- \`\`\`js
101- const createElementId = (length) => {
102- \treturn Math.random()
103- \t\t.toString(36)
104- \t\t.replace(/[^a-z]+/g, '')
105- \t\t.slice(0, length || 5)
106- }
107- \`\`\`
108- `,
109- }
110- },
111- methods: {
112- handleInteraction(id) {
113- const parentId = id.split('-markdown-input-')[0]
114- const index = Array.from(document.querySelectorAll(`span[id^="${parentId}-markdown-input-"]`)).findIndex((el) => el.id.includes(id))
115- if (index === -1 ) {
116- return
117- }
118- let checkBoxIndex = 0
119- let lines = this.text.split('\n')
120- for (let i = 0; i < lines.length; i++) {
121- if (lines[i].includes('[ ]') || lines[i].includes('[x]')) {
122- if (checkBoxIndex === index) {
123- const isChecked = lines[i].includes('[x]')
124- if (isChecked) {
125- lines[i] = lines[i].replace('[x]', '[ ]')
126- } else {
127- lines[i] = lines[i].replace('[ ]', '[x]')
128- }
129- break
130- }
131- checkBoxIndex++
132- }
133- }
134- this.text = lines.join('\n')
135- },
136- },
137- }
138- </script>
139- <style lang="scss">
140- textarea {
141- width: 100%;
142- height: 200px;
143- }
144- </style>
145- ```
146-
147- ### Usage with NcRichContenteditable
148-
149- See [NcRichContenteditable](#/Components/NcRichContenteditable) documentation for more information
150-
151- ```vue
152- <template>
153- <div>
154- <NcRichContenteditable v-model="message"
155- :auto-complete="autoComplete"
156- :maxlength="100"
157- :user-data="userData"
158- placeholder="Try mentioning user @Test01 or inserting emoji :smile"
159- @submit="onSubmit" />
160-
161- <NcCheckboxRadioSwitch v-model="autolink" type="checkbox">Autolink</NcCheckboxRadioSwitch>
162- <NcCheckboxRadioSwitch v-model="useMarkdown" type="checkbox">Use Markdown</NcCheckboxRadioSwitch>
163- <NcCheckboxRadioSwitch v-model="useExtendedMarkdown" type="checkbox">Use extended Markdown</NcCheckboxRadioSwitch>
164-
165- <NcRichText :text="text"
166- :autolink="autolink"
167- :arguments="userMentions"
168- :use-markdown="useMarkdown"
169- :use-extended-markdown="useExtendedMarkdown" />
170- </div>
171- </template>
172- <script>
173- export default {
174- data() {
175- return {
176- message: '',
177- autolink: true,
178- useMarkdown: true,
179- useExtendedMarkdown: true,
180- userData: {
181- Test01: {
182- icon: 'icon-user',
183- id: 'Test01',
184- label: 'Test01',
185- source: 'users',
186- primary: true,
187- },
188- Test02: {
189- icon: 'icon-user',
190- id: 'Test02',
191- label: 'Test02',
192- source: 'users',
193- status: {
194- clearAt: null,
195- icon: '🎡',
196- message: 'Visiting London',
197- status: 'away',
198- },
199- subline: 'Visiting London',
200- },
201- 'Test@User': {
202- icon: 'icon-user',
203- id: 'Test@User',
204- label: 'Test 03',
205- source: 'users',
206- status: {
207- clearAt: null,
208- icon: '🎡',
209- message: 'Having space in my name',
210- status: 'online',
211- },
212- subline: 'Visiting London',
213- },
214- 'Test Offline': {
215- icon: 'icon-user',
216- id: 'Test Offline',
217- label: 'Test Offline',
218- source: 'users',
219- status: {
220- clearAt: null,
221- icon: null,
222- message: null,
223- status: 'offline',
224- },
225- subline: null,
226- },
227- 'Test DND': {
228- icon: 'icon-user',
229- id: 'Test DND',
230- label: 'Test DND',
231- source: 'users',
232- status: {
233- clearAt: null,
234- icon: null,
235- message: 'Out sick',
236- status: 'dnd',
237- },
238- subline: 'Out sick',
239- },
240- },
241- userMentions: {
242- 'user-1': {
243- component: 'NcUserBubble',
244- props: {
245- displayName: 'Test01',
246- user: 'Test01',
247- primary: true,
248- },
249- },
250- 'user-2': {
251- component: 'NcUserBubble',
252- props: {
253- displayName: 'Test02',
254- user: 'Test02',
255- },
256- },
257- 'user-3': {
258- component: 'NcUserBubble',
259- props: {
260- displayName: 'Test 03',
261- user: 'Test@User',
262- },
263- },
264- 'user-4': {
265- component: 'NcUserBubble',
266- props: {
267- displayName: 'Test Offline',
268- user: 'Test Offline',
269- },
270- },
271- 'user-5': {
272- component: 'NcUserBubble',
273- props: {
274- displayName: 'Test DND',
275- user: 'Test DND',
276- },
277- },
278- },
279- }
280- },
281- computed: {
282- text() {
283- return this.message
284- .replace('@Test01', '{user-1}')
285- .replace('@Test02', '{user-2}')
286- .replace('@Test@User', '{user-3}')
287- .replace('@"Test Offline"', '{user-4}')
288- .replace('@"Test DND"', '{user-5}')
289- },
290- },
291- methods: {
292- autoComplete(search, callback) {
293- callback(Object.values(this.userData))
294- },
295- onSubmit() {
296- alert(this.message)
297- }
298- }
299- }
300- </script>
301- ```
30273</docs >
30374
30475<script >
@@ -491,7 +262,25 @@ export default {
491262 // escape special symbol "<" to not treat text as HTML
492263 .replace (/ <[^ >] + >/ g , (match ) => match .replace (/ </ g , ' <' ))
493264 // unescape special symbol ">" to parse blockquotes
494- .replace (/ >/ gmi , ' >' ))
265+ .replace (/ >/ gmi , ' >' )
266+ // ordered list
267+ .replace (/ \b (\d + )\. \s / g , (match , p1 , offset , string ) => {
268+ const following = string .slice (offset + match .length )
269+ const nextMatch = following .match (/ \b (\d + )\. \s / )? .[1 ]
270+
271+ if (! nextMatch) {
272+ return match
273+ }
274+ // If the next number is not +1, we fix the current number to be +1 of the previous one
275+ // This ensures that lists are always rendered correctly, even if the input is wrong
276+ console .log (parseInt (p1, 10 ) + 1 , parseInt (nextMatch, 10 ), parseInt (p1, 10 ) + 1 !== parseInt (nextMatch, 10 ))
277+ if (parseInt (p1, 10 ) + 1 !== parseInt (nextMatch, 10 )) {
278+ console .log (' fixing |' , p1 + ' \\ . ' )
279+ return p1 + ' \\ . '
280+ }
281+ return match
282+ })
283+ )
495284 .result
496285
497286 return h (' div' , { class: ' rich-text--wrapper rich-text--wrapper-markdown' }, [
@@ -537,6 +326,8 @@ export default {
537326 },
538327
539328 createElement (type , props , key ) {
329+ console .log (type, props, key)
330+
540331 // Modified code from vue/jsx-runtime
541332 if (key) {
542333 props .key = key
0 commit comments