@nextcloud/vue 3.10.2 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -19
- package/README.md +9 -10
- package/dist/Components/ActionButton.js +24 -3
- package/dist/Components/ActionButton.js.map +1 -1
- package/dist/Components/ActionCaption.js +23 -0
- package/dist/Components/ActionCaption.js.map +1 -0
- package/dist/Components/ActionCheckbox.js +24 -3
- package/dist/Components/ActionCheckbox.js.map +1 -1
- package/dist/Components/ActionInput.js +124 -21
- package/dist/Components/ActionInput.js.map +1 -1
- package/dist/Components/ActionLink.js +24 -3
- package/dist/Components/ActionLink.js.map +1 -1
- package/dist/Components/ActionRadio.js +24 -3
- package/dist/Components/ActionRadio.js.map +1 -1
- package/dist/Components/ActionRouter.js +23 -2
- package/dist/Components/ActionRouter.js.map +1 -1
- package/dist/Components/ActionSeparator.js +1 -1
- package/dist/Components/ActionSeparator.js.map +1 -1
- package/dist/Components/ActionText.js +23 -2
- package/dist/Components/ActionText.js.map +1 -1
- package/dist/Components/ActionTextEditable.js +45 -3
- package/dist/Components/ActionTextEditable.js.map +1 -1
- package/dist/Components/Actions.js +25 -4
- package/dist/Components/Actions.js.map +1 -1
- package/dist/Components/AppContent.js +21 -1
- package/dist/Components/AppContent.js.map +1 -1
- package/dist/Components/AppContentDetails.js +1 -1
- package/dist/Components/AppContentDetails.js.map +1 -1
- package/dist/Components/AppContentList.js +1 -1
- package/dist/Components/AppContentList.js.map +1 -1
- package/dist/Components/AppNavigation.js +109 -2
- package/dist/Components/AppNavigation.js.map +1 -1
- package/dist/Components/AppNavigationCaption.js +66 -1
- package/dist/Components/AppNavigationCaption.js.map +1 -1
- package/dist/Components/AppNavigationCounter.js +2 -2
- package/dist/Components/AppNavigationCounter.js.map +1 -1
- package/dist/Components/AppNavigationIconBullet.js +2 -2
- package/dist/Components/AppNavigationIconBullet.js.map +1 -1
- package/dist/Components/AppNavigationItem.js +49 -7
- package/dist/Components/AppNavigationItem.js.map +1 -1
- package/dist/Components/AppNavigationNew.js +1 -1
- package/dist/Components/AppNavigationNew.js.map +1 -1
- package/dist/Components/AppNavigationNewItem.js +2 -2
- package/dist/Components/AppNavigationNewItem.js.map +1 -1
- package/dist/Components/AppNavigationSettings.js +5 -5
- package/dist/Components/AppNavigationSettings.js.map +1 -1
- package/dist/Components/AppNavigationSpacer.js +1 -1
- package/dist/Components/AppNavigationSpacer.js.map +1 -1
- package/dist/Components/AppNavigationToggle.js +109 -2
- package/dist/Components/AppNavigationToggle.js.map +1 -1
- package/dist/Components/AppSettingsDialog.js +64 -22
- package/dist/Components/AppSettingsDialog.js.map +1 -1
- package/dist/Components/AppSettingsSection.js +1 -1
- package/dist/Components/AppSettingsSection.js.map +1 -1
- package/dist/Components/AppSidebar.js +38 -17
- package/dist/Components/AppSidebar.js.map +1 -1
- package/dist/Components/AppSidebarTab.js +1 -1
- package/dist/Components/AppSidebarTab.js.map +1 -1
- package/dist/Components/Avatar.js +9 -10
- package/dist/Components/Avatar.js.map +1 -1
- package/dist/Components/Breadcrumb.js +33 -12
- package/dist/Components/Breadcrumb.js.map +1 -1
- package/dist/Components/Breadcrumbs.js +59 -17
- package/dist/Components/Breadcrumbs.js.map +1 -1
- package/dist/Components/CheckboxRadioSwitch.js +44 -0
- package/dist/Components/CheckboxRadioSwitch.js.map +1 -0
- package/dist/Components/ColorPicker.js +6 -7
- package/dist/Components/ColorPicker.js.map +1 -1
- package/dist/Components/Content.js +2 -2
- package/dist/Components/Content.js.map +1 -1
- package/dist/Components/CounterBubble.js +23 -0
- package/dist/Components/CounterBubble.js.map +1 -0
- package/dist/Components/DatetimePicker.js +341 -3
- package/dist/Components/DatetimePicker.js.map +1 -1
- package/dist/Components/EmojiPicker.js +14 -14
- package/dist/Components/EmojiPicker.js.map +1 -1
- package/dist/Components/EmptyContent.js +2 -2
- package/dist/Components/EmptyContent.js.map +1 -1
- package/dist/Components/Highlight.js +3 -3
- package/dist/Components/Highlight.js.map +1 -1
- package/dist/Components/ListItem.js +130 -0
- package/dist/Components/ListItem.js.map +1 -0
- package/dist/Components/ListItemIcon.js +20 -21
- package/dist/Components/ListItemIcon.js.map +1 -1
- package/dist/Components/Modal.js +53 -11
- package/dist/Components/Modal.js.map +1 -1
- package/dist/Components/Multiselect.js +22 -23
- package/dist/Components/Multiselect.js.map +1 -1
- package/dist/Components/MultiselectTags.js +24 -25
- package/dist/Components/MultiselectTags.js.map +1 -1
- package/dist/Components/Popover.js +2 -2
- package/dist/Components/Popover.js.map +1 -1
- package/dist/Components/PopoverMenu.js +1 -1
- package/dist/Components/PopoverMenu.js.map +1 -1
- package/dist/Components/ProgressBar.js +2 -2
- package/dist/Components/ProgressBar.js.map +1 -1
- package/dist/Components/RichContenteditable.js +2 -2
- package/dist/Components/RichContenteditable.js.map +1 -1
- package/dist/Components/SettingsInputText.js +22 -1
- package/dist/Components/SettingsInputText.js.map +1 -1
- package/dist/Components/SettingsSection.js +2 -2
- package/dist/Components/SettingsSection.js.map +1 -1
- package/dist/Components/SettingsSelectGroup.js +41 -21
- package/dist/Components/SettingsSelectGroup.js.map +1 -1
- package/dist/Components/TimezonePicker.js +340 -0
- package/dist/Components/TimezonePicker.js.map +1 -0
- package/dist/Components/UserBubble.js +35 -36
- package/dist/Components/UserBubble.js.map +1 -1
- package/dist/Directives/Focus.js +1 -1
- package/dist/Directives/Focus.js.map +1 -1
- package/dist/Directives/Linkify.js +1 -1
- package/dist/Directives/Linkify.js.map +1 -1
- package/dist/Directives/Tooltip.js +2 -2
- package/dist/Directives/Tooltip.js.map +1 -1
- package/dist/Functions/usernameToColor.js +2 -3
- package/dist/Functions/usernameToColor.js.map +1 -1
- package/dist/Mixins/excludeClickOutsideClasses.js +2 -2
- package/dist/Mixins/excludeClickOutsideClasses.js.map +1 -1
- package/dist/Mixins/isFullscreen.js +1 -1
- package/dist/Mixins/isFullscreen.js.map +1 -1
- package/dist/Mixins/isMobile.js +1 -1
- package/dist/Mixins/isMobile.js.map +1 -1
- package/dist/Mixins/richEditor.js +1 -1
- package/dist/Mixins/richEditor.js.map +1 -1
- package/dist/ncvuecomponents.js +276 -69
- package/dist/ncvuecomponents.js.map +1 -1
- package/package.json +28 -43
- package/src/assets/action.scss +0 -132
- package/src/assets/iconfont/README.md +0 -30
- package/src/assets/iconfont/arrow-left-double.svg +0 -3
- package/src/assets/iconfont/arrow-left.svg +0 -3
- package/src/assets/iconfont/arrow-right-double.svg +0 -3
- package/src/assets/iconfont/arrow-right.svg +0 -3
- package/src/assets/iconfont/breadcrumb.svg +0 -1
- package/src/assets/iconfont/checkmark.svg +0 -1
- package/src/assets/iconfont/close.svg +0 -1
- package/src/assets/iconfont/confirm.svg +0 -1
- package/src/assets/iconfont/info.svg +0 -1
- package/src/assets/iconfont/menu.svg +0 -1
- package/src/assets/iconfont/more.svg +0 -1
- package/src/assets/iconfont/pause.svg +0 -1
- package/src/assets/iconfont/play.svg +0 -1
- package/src/assets/iconfont/triangle-s.svg +0 -1
- package/src/assets/iconfont/user-status-away.svg +0 -2
- package/src/assets/iconfont/user-status-dnd.svg +0 -2
- package/src/assets/iconfont/user-status-invisible.svg +0 -2
- package/src/assets/iconfont/user-status-online.svg +0 -2
- package/src/assets/inputs.scss +0 -104
- package/src/assets/variables.scss +0 -57
- package/src/components/ActionButton/ActionButton.vue +0 -160
- package/src/components/ActionButton/index.js +0 -24
- package/src/components/ActionCheckbox/ActionCheckbox.vue +0 -220
- package/src/components/ActionCheckbox/index.js +0 -24
- package/src/components/ActionInput/ActionInput.vue +0 -418
- package/src/components/ActionInput/index.js +0 -24
- package/src/components/ActionLink/ActionLink.vue +0 -132
- package/src/components/ActionLink/index.js +0 -24
- package/src/components/ActionRadio/ActionRadio.vue +0 -219
- package/src/components/ActionRadio/index.js +0 -24
- package/src/components/ActionRouter/ActionRouter.vue +0 -100
- package/src/components/ActionRouter/index.js +0 -24
- package/src/components/ActionSeparator/ActionSeparator.vue +0 -43
- package/src/components/ActionSeparator/index.js +0 -24
- package/src/components/ActionText/ActionText.vue +0 -87
- package/src/components/ActionText/index.js +0 -24
- package/src/components/ActionTextEditable/ActionTextEditable.vue +0 -306
- package/src/components/ActionTextEditable/index.js +0 -24
- package/src/components/Actions/Actions.vue +0 -764
- package/src/components/Actions/index.js +0 -24
- package/src/components/AppContent/AppContent.vue +0 -93
- package/src/components/AppContent/index.js +0 -23
- package/src/components/AppContentDetails/AppContentDetails.vue +0 -34
- package/src/components/AppContentDetails/index.js +0 -23
- package/src/components/AppContentList/AppContentList.vue +0 -44
- package/src/components/AppContentList/index.js +0 -23
- package/src/components/AppNavigation/AppNavigation.vue +0 -190
- package/src/components/AppNavigation/index.js +0 -23
- package/src/components/AppNavigationCaption/AppNavigationCaption.vue +0 -39
- package/src/components/AppNavigationCaption/index.js +0 -3
- package/src/components/AppNavigationCounter/AppNavigationCounter.vue +0 -82
- package/src/components/AppNavigationCounter/index.js +0 -25
- package/src/components/AppNavigationIconBullet/AppNavigationIconBullet.vue +0 -95
- package/src/components/AppNavigationIconBullet/index.js +0 -24
- package/src/components/AppNavigationItem/AppNavigationIconCollapsible.vue +0 -90
- package/src/components/AppNavigationItem/AppNavigationItem.vue +0 -629
- package/src/components/AppNavigationItem/InputConfirmCancel.vue +0 -134
- package/src/components/AppNavigationItem/index.js +0 -24
- package/src/components/AppNavigationNew/AppNavigationNew.vue +0 -76
- package/src/components/AppNavigationNew/index.js +0 -23
- package/src/components/AppNavigationNewItem/AppNavigationNewItem.vue +0 -165
- package/src/components/AppNavigationNewItem/index.js +0 -24
- package/src/components/AppNavigationSettings/AppNavigationSettings.vue +0 -105
- package/src/components/AppNavigationSettings/index.js +0 -23
- package/src/components/AppNavigationSpacer/AppNavigationSpacer.vue +0 -39
- package/src/components/AppNavigationSpacer/index.js +0 -23
- package/src/components/AppNavigationToggle/AppNavigationToggle.vue +0 -78
- package/src/components/AppNavigationToggle/index.js +0 -24
- package/src/components/AppSettingsDialog/AppSettingsDialog.vue +0 -331
- package/src/components/AppSettingsDialog/index.js +0 -25
- package/src/components/AppSettingsSection/AppSettingsSection.vue +0 -64
- package/src/components/AppSettingsSection/index.js +0 -25
- package/src/components/AppSidebar/AppSidebar.vue +0 -802
- package/src/components/AppSidebar/AppSidebarTabs.vue +0 -348
- package/src/components/AppSidebar/index.js +0 -23
- package/src/components/AppSidebarTab/AppSidebarTab.vue +0 -103
- package/src/components/AppSidebarTab/index.js +0 -23
- package/src/components/Avatar/Avatar.vue +0 -758
- package/src/components/Avatar/index.js +0 -25
- package/src/components/Breadcrumb/Breadcrumb.vue +0 -262
- package/src/components/Breadcrumb/index.js +0 -25
- package/src/components/Breadcrumbs/Breadcrumbs.vue +0 -537
- package/src/components/Breadcrumbs/index.js +0 -25
- package/src/components/ColorPicker/ColorPicker.vue +0 -380
- package/src/components/ColorPicker/index.js +0 -25
- package/src/components/Content/Content.vue +0 -77
- package/src/components/Content/index.js +0 -23
- package/src/components/DatetimePicker/DatetimePicker.vue +0 -195
- package/src/components/DatetimePicker/index.js +0 -28
- package/src/components/DatetimePicker/index.scss +0 -405
- package/src/components/EmojiPicker/EmojiPicker.vue +0 -302
- package/src/components/EmojiPicker/index.js +0 -23
- package/src/components/EmptyContent/EmptyContent.vue +0 -120
- package/src/components/EmptyContent/index.js +0 -24
- package/src/components/Highlight/Highlight.vue +0 -183
- package/src/components/Highlight/index.js +0 -25
- package/src/components/ListItemIcon/ListItemIcon.vue +0 -277
- package/src/components/ListItemIcon/index.js +0 -25
- package/src/components/Modal/Modal.vue +0 -833
- package/src/components/Modal/index.js +0 -27
- package/src/components/Multiselect/EllipsisedOption.vue +0 -141
- package/src/components/Multiselect/Multiselect.vue +0 -430
- package/src/components/Multiselect/index.js +0 -28
- package/src/components/Multiselect/index.scss +0 -290
- package/src/components/MultiselectTags/MultiselectTags.vue +0 -179
- package/src/components/MultiselectTags/api.js +0 -115
- package/src/components/MultiselectTags/index.js +0 -23
- package/src/components/Popover/Popover.vue +0 -208
- package/src/components/Popover/index.js +0 -25
- package/src/components/PopoverMenu/PopoverMenu.vue +0 -62
- package/src/components/PopoverMenu/PopoverMenuItem.vue +0 -382
- package/src/components/PopoverMenu/index.js +0 -24
- package/src/components/ProgressBar/ProgressBar.vue +0 -135
- package/src/components/ProgressBar/index.js +0 -25
- package/src/components/RichContenteditable/AutoCompleteResult.vue +0 -191
- package/src/components/RichContenteditable/MentionBubble.vue +0 -165
- package/src/components/RichContenteditable/RichContenteditable.vue +0 -517
- package/src/components/RichContenteditable/index.js +0 -25
- package/src/components/SettingsInputText/SettingsInputText.vue +0 -207
- package/src/components/SettingsInputText/index.js +0 -24
- package/src/components/SettingsSection/SettingsSection.vue +0 -151
- package/src/components/SettingsSection/index.js +0 -24
- package/src/components/SettingsSelectGroup/SettingsSelectGroup.vue +0 -149
- package/src/components/SettingsSelectGroup/index.js +0 -25
- package/src/components/UserBubble/UserBubble.vue +0 -319
- package/src/components/UserBubble/index.js +0 -25
- package/src/components/index.js +0 -110
- package/src/directives/Focus/index.js +0 -29
- package/src/directives/Linkify/index.js +0 -31
- package/src/directives/Tooltip/index.js +0 -32
- package/src/directives/Tooltip/index.scss +0 -117
- package/src/directives/index.js +0 -31
- package/src/fonts/iconfont-vue-f56d517.eot +0 -0
- package/src/fonts/iconfont-vue-f56d517.svg +0 -1
- package/src/fonts/iconfont-vue-f56d517.ttf +0 -0
- package/src/fonts/iconfont-vue-f56d517.woff +0 -0
- package/src/fonts/scss/iconfont-vue.scss +0 -115
- package/src/functions/usernameToColor/index.js +0 -25
- package/src/functions/usernameToColor/usernameToColor.js +0 -68
- package/src/index.js +0 -40
- package/src/l10n.js +0 -42
- package/src/mixins/actionGlobal.js +0 -59
- package/src/mixins/actionText.js +0 -85
- package/src/mixins/excludeClickOutsideClasses/index.js +0 -72
- package/src/mixins/index.js +0 -35
- package/src/mixins/isFullscreen/index.js +0 -46
- package/src/mixins/isMobile/index.js +0 -43
- package/src/mixins/l10n.js +0 -8
- package/src/mixins/richEditor/index.js +0 -160
- package/src/mixins/userStatus.js +0 -76
- package/src/utils/FindRanges.js +0 -47
- package/src/utils/GenColors.js +0 -79
- package/src/utils/GenRandomId.js +0 -31
- package/src/utils/GetChildren.js +0 -47
- package/src/utils/GetParent.js +0 -41
- package/src/utils/IsMobileState.js +0 -49
- package/src/utils/IsOutOfViewport.js +0 -36
- package/src/utils/ScopeComponent.js +0 -37
- package/src/utils/Timer.js +0 -61
- package/src/utils/ValidateChildren.js +0 -50
- package/src/utils/ValidateSlot.js +0 -57
|
@@ -1,517 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
|
|
3
|
-
-
|
|
4
|
-
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
|
5
|
-
-
|
|
6
|
-
- @license GNU AGPL version 3 or any later version
|
|
7
|
-
-
|
|
8
|
-
- This program is free software: you can redistribute it and/or modify
|
|
9
|
-
- it under the terms of the GNU Affero General Public License as
|
|
10
|
-
- published by the Free Software Foundation, either version 3 of the
|
|
11
|
-
- License, or (at your option) any later version.
|
|
12
|
-
-
|
|
13
|
-
- This program is distributed in the hope that it will be useful,
|
|
14
|
-
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
-
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
-
- GNU Affero General Public License for more details.
|
|
17
|
-
-
|
|
18
|
-
- You should have received a copy of the GNU Affero General Public License
|
|
19
|
-
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20
|
-
-->
|
|
21
|
-
|
|
22
|
-
<docs>
|
|
23
|
-
|
|
24
|
-
### General description
|
|
25
|
-
|
|
26
|
-
This component displays contenteditable div with automated @ autocompletion [at].
|
|
27
|
-
|
|
28
|
-
### Examples
|
|
29
|
-
|
|
30
|
-
```vue
|
|
31
|
-
<template>
|
|
32
|
-
<div>
|
|
33
|
-
<RichContenteditable
|
|
34
|
-
v-model="message"
|
|
35
|
-
:auto-complete="autoComplete"
|
|
36
|
-
:maxlength="100"
|
|
37
|
-
:user-data="userData"
|
|
38
|
-
placeholder="Try mentioning the user Test01"
|
|
39
|
-
@submit="onSubmit" />
|
|
40
|
-
<br>
|
|
41
|
-
|
|
42
|
-
<RichContenteditable
|
|
43
|
-
v-model="message"
|
|
44
|
-
:auto-complete="autoComplete"
|
|
45
|
-
:maxlength="400"
|
|
46
|
-
:multiline="true"
|
|
47
|
-
:user-data="userData"
|
|
48
|
-
placeholder="Try mentioning the user Test01"
|
|
49
|
-
@submit="onSubmit" />
|
|
50
|
-
<br>
|
|
51
|
-
<br>
|
|
52
|
-
{{ JSON.stringify(message) }}
|
|
53
|
-
</div>
|
|
54
|
-
</template>
|
|
55
|
-
<script>
|
|
56
|
-
export default {
|
|
57
|
-
data() {
|
|
58
|
-
return {
|
|
59
|
-
message: 'Lorem ipsum dolor sit amet.',
|
|
60
|
-
|
|
61
|
-
// You need to provide this for the inline
|
|
62
|
-
// mention to understand what to display or not.
|
|
63
|
-
userData: {
|
|
64
|
-
Test01: {
|
|
65
|
-
icon: 'icon-user',
|
|
66
|
-
id: 'Test01',
|
|
67
|
-
label: 'Test01',
|
|
68
|
-
source: 'users',
|
|
69
|
-
primary: true,
|
|
70
|
-
},
|
|
71
|
-
Test02: {
|
|
72
|
-
icon: 'icon-user',
|
|
73
|
-
id: 'Test02',
|
|
74
|
-
label: 'Test02',
|
|
75
|
-
source: 'users',
|
|
76
|
-
status: {
|
|
77
|
-
clearAt: null,
|
|
78
|
-
icon: '🎡',
|
|
79
|
-
message: 'Visiting London',
|
|
80
|
-
status: 'away',
|
|
81
|
-
},
|
|
82
|
-
subline: 'Visiting London',
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
methods: {
|
|
88
|
-
/**
|
|
89
|
-
* Do your own query to the autocompletion api.
|
|
90
|
-
* The returned data bellow is a fake data example.
|
|
91
|
-
* The callback expects the same format returned by the core/autocomplete/get ocs api endpoint!
|
|
92
|
-
* @see userData example above
|
|
93
|
-
*
|
|
94
|
-
* @param {string} search the query
|
|
95
|
-
* @param {Function} callback the callback to process the results with
|
|
96
|
-
*/
|
|
97
|
-
autoComplete(search, callback) {
|
|
98
|
-
callback(Object.values(this.userData))
|
|
99
|
-
},
|
|
100
|
-
onSubmit() {
|
|
101
|
-
alert(this.message)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
</script>
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
</docs>
|
|
109
|
-
|
|
110
|
-
<template>
|
|
111
|
-
<div ref="contenteditable"
|
|
112
|
-
v-tooltip="tooltip"
|
|
113
|
-
:class="{
|
|
114
|
-
'rich-contenteditable__input--empty': isEmptyValue,
|
|
115
|
-
'rich-contenteditable__input--multiline': multiline,
|
|
116
|
-
'rich-contenteditable__input--overflow': isOverMaxlength,
|
|
117
|
-
}"
|
|
118
|
-
:contenteditable="contenteditable"
|
|
119
|
-
:placeholder="placeholder"
|
|
120
|
-
aria-multiline="true"
|
|
121
|
-
class="rich-contenteditable__input"
|
|
122
|
-
role="textbox"
|
|
123
|
-
@input="onInput"
|
|
124
|
-
v-on="$listeners"
|
|
125
|
-
@keydown.delete="onDelete"
|
|
126
|
-
@keydown.enter.exact="onEnter"
|
|
127
|
-
@keydown.ctrl.enter.exact.stop.prevent="onCtrlEnter"
|
|
128
|
-
@paste="onPaste" />
|
|
129
|
-
</template>
|
|
130
|
-
|
|
131
|
-
<script>
|
|
132
|
-
import Tribute from 'tributejs/dist/tribute.esm'
|
|
133
|
-
import debounce from 'debounce'
|
|
134
|
-
import stringLength from 'string-length'
|
|
135
|
-
|
|
136
|
-
import { t } from '../../l10n.js'
|
|
137
|
-
import AutoCompleteResult from './AutoCompleteResult'
|
|
138
|
-
import richEditor from '../../mixins/richEditor/index'
|
|
139
|
-
|
|
140
|
-
export default {
|
|
141
|
-
name: 'RichContenteditable',
|
|
142
|
-
|
|
143
|
-
mixins: [richEditor],
|
|
144
|
-
|
|
145
|
-
props: {
|
|
146
|
-
value: {
|
|
147
|
-
type: String,
|
|
148
|
-
default: '',
|
|
149
|
-
required: true,
|
|
150
|
-
},
|
|
151
|
-
placeholder: {
|
|
152
|
-
type: String,
|
|
153
|
-
default: t('Write message, @ to mention someone …'),
|
|
154
|
-
},
|
|
155
|
-
autoComplete: {
|
|
156
|
-
type: Function,
|
|
157
|
-
required: true,
|
|
158
|
-
},
|
|
159
|
-
menuContainer: {
|
|
160
|
-
type: Element,
|
|
161
|
-
default: () => document.body,
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Make the contenteditable looks like a textarea or not.
|
|
166
|
-
* Default looks like a single-line input.
|
|
167
|
-
* This also handle the default enter/shift+enter behaviour.
|
|
168
|
-
* if multiline, enter = newline; otherwise enter = submit
|
|
169
|
-
* shift+enter always add a new line. ctrl+enter always submits
|
|
170
|
-
*/
|
|
171
|
-
multiline: {
|
|
172
|
-
type: Boolean,
|
|
173
|
-
default: false,
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Is the content editable ?
|
|
178
|
-
*/
|
|
179
|
-
contenteditable: {
|
|
180
|
-
type: Boolean,
|
|
181
|
-
default: true,
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Max allowed length
|
|
186
|
-
*/
|
|
187
|
-
maxlength: {
|
|
188
|
-
type: Number,
|
|
189
|
-
default: null,
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
data() {
|
|
194
|
-
return {
|
|
195
|
-
tribute: null,
|
|
196
|
-
options: {
|
|
197
|
-
fillAttr: 'id',
|
|
198
|
-
// Search against id and label (display name)
|
|
199
|
-
lookup: result => `${result.id} ${result.label}`,
|
|
200
|
-
// Where to inject the menu popup
|
|
201
|
-
menuContainer: this.menuContainer,
|
|
202
|
-
// Popup mention autocompletion templates
|
|
203
|
-
menuItemTemplate: item => this.renderComponentHtml(item.original, AutoCompleteResult),
|
|
204
|
-
// Hide if no results
|
|
205
|
-
noMatchTemplate: () => '<span class="hidden"></span>',
|
|
206
|
-
// Inner display of mentions
|
|
207
|
-
selectTemplate: item => this.genSelectTemplate(item?.original?.id),
|
|
208
|
-
// Autocompletion results
|
|
209
|
-
values: this.debouncedAutoComplete,
|
|
210
|
-
},
|
|
211
|
-
|
|
212
|
-
// Represent the raw untrimmed text of the contenteditable
|
|
213
|
-
// serves no other purpose than to check whether the
|
|
214
|
-
// content is empty or not
|
|
215
|
-
localValue: this.value,
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
computed: {
|
|
220
|
-
/**
|
|
221
|
-
* Is the current trimmed value empty?
|
|
222
|
-
* @returns {boolean}
|
|
223
|
-
*/
|
|
224
|
-
isEmptyValue() {
|
|
225
|
-
return !this.localValue
|
|
226
|
-
|| (this.localValue && this.localValue.trim() === '')
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Is this Firefox? 🙄
|
|
231
|
-
* @returns {boolean}
|
|
232
|
-
*/
|
|
233
|
-
isFF() {
|
|
234
|
-
return !!navigator.userAgent.match(/firefox/i)
|
|
235
|
-
},
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Is the current value over maxlength?
|
|
239
|
-
* @returns {boolean}
|
|
240
|
-
*/
|
|
241
|
-
isOverMaxlength() {
|
|
242
|
-
if (this.isEmptyValue || !this.maxlength) {
|
|
243
|
-
return false
|
|
244
|
-
}
|
|
245
|
-
return stringLength(this.localValue) > this.maxlength
|
|
246
|
-
},
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Tooltip to show if characters count is over limit
|
|
250
|
-
* @returns {string}
|
|
251
|
-
*/
|
|
252
|
-
tooltip() {
|
|
253
|
-
if (!this.isOverMaxlength) {
|
|
254
|
-
return null
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
content: t('Message limit of {count} characters reached', { count: this.maxlength }),
|
|
258
|
-
show: true,
|
|
259
|
-
trigger: 'manual',
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
|
|
264
|
-
watch: {
|
|
265
|
-
/**
|
|
266
|
-
* If the parent value change, we compare the plain text rendering
|
|
267
|
-
* If it's different, we render everything and update the main content
|
|
268
|
-
*/
|
|
269
|
-
value() {
|
|
270
|
-
const html = this.$refs.contenteditable.innerHTML
|
|
271
|
-
// Compare trimmed versions to be safe
|
|
272
|
-
if (this.value.trim() !== this.parseContent(html).trim()) {
|
|
273
|
-
this.updateContent(this.value)
|
|
274
|
-
}
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
|
|
278
|
-
mounted() {
|
|
279
|
-
this.tribute = new Tribute(this.options)
|
|
280
|
-
this.tribute.attach(this.$el)
|
|
281
|
-
|
|
282
|
-
// Update default value
|
|
283
|
-
this.updateContent(this.value)
|
|
284
|
-
|
|
285
|
-
// Removes the contenteditable attribute at first load if the prop is
|
|
286
|
-
// set to false.
|
|
287
|
-
this.$refs.contenteditable.contentEditable = this.contenteditable
|
|
288
|
-
},
|
|
289
|
-
beforeDestroy() {
|
|
290
|
-
if (this.tribute) {
|
|
291
|
-
this.tribute.detach(this.$el)
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
methods: {
|
|
296
|
-
/**
|
|
297
|
-
* Re-emit the input event to the parent
|
|
298
|
-
* @param {Event} event the input event
|
|
299
|
-
*/
|
|
300
|
-
onInput(event) {
|
|
301
|
-
this.updateValue(event.target.innerHTML)
|
|
302
|
-
},
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* When pasting, sanitize the content, extract text
|
|
306
|
-
* and render it again
|
|
307
|
-
* @param {Event} event the paste event
|
|
308
|
-
* @emits {Event} paste the original paste event
|
|
309
|
-
*/
|
|
310
|
-
onPaste(event) {
|
|
311
|
-
event.preventDefault()
|
|
312
|
-
const clipboardData = event.clipboardData
|
|
313
|
-
|
|
314
|
-
/** The original paste event */
|
|
315
|
-
this.$emit('paste', event)
|
|
316
|
-
|
|
317
|
-
// If we have a file or if we don't have any text, ignore
|
|
318
|
-
if (clipboardData.files.length !== 0
|
|
319
|
-
|| !Object.values(clipboardData.items).find(item => item?.type.startsWith('text'))) {
|
|
320
|
-
return
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const html = clipboardData.getData('text')
|
|
324
|
-
const selection = window.getSelection()
|
|
325
|
-
|
|
326
|
-
// If no selection, replace the whole data
|
|
327
|
-
if (!selection.rangeCount) {
|
|
328
|
-
this.updateValue(html)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Generate text and insert
|
|
332
|
-
const text = this.parseContent(html)
|
|
333
|
-
const range = selection.getRangeAt(0)
|
|
334
|
-
selection.deleteFromDocument()
|
|
335
|
-
range.insertNode(document.createTextNode(text))
|
|
336
|
-
|
|
337
|
-
// Put cursor at the end of the selection
|
|
338
|
-
const newRange = document.createRange()
|
|
339
|
-
newRange.setStart(event.target, range.endOffset)
|
|
340
|
-
newRange.collapse(true)
|
|
341
|
-
selection.removeAllRanges()
|
|
342
|
-
selection.addRange(newRange)
|
|
343
|
-
|
|
344
|
-
// Propagate data
|
|
345
|
-
this.updateValue(event.target.innerHTML)
|
|
346
|
-
},
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Update the value text from the provided html
|
|
350
|
-
* @param {string} htmlOrText the html content (or raw text with @mentions)
|
|
351
|
-
*/
|
|
352
|
-
updateValue(htmlOrText) {
|
|
353
|
-
const text = this.parseContent(htmlOrText)
|
|
354
|
-
this.localValue = text
|
|
355
|
-
this.$emit('input', text)
|
|
356
|
-
this.$emit('update:value', text)
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Update content and local value
|
|
361
|
-
* @param {string} value the message value
|
|
362
|
-
*/
|
|
363
|
-
updateContent(value) {
|
|
364
|
-
const renderedContent = this.renderContent(value)
|
|
365
|
-
this.$refs.contenteditable.innerHTML = renderedContent
|
|
366
|
-
this.localValue = value
|
|
367
|
-
},
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Because FF have a decade old bug preventing contenteditable=false
|
|
371
|
-
* to properly be deleted on backspace, we have to hack 👀
|
|
372
|
-
* https://stackoverflow.com/a/59383394/3885878
|
|
373
|
-
* https://stackoverflow.com/a/30574622
|
|
374
|
-
*
|
|
375
|
-
* @param {Event} event the delete keydown event
|
|
376
|
-
*/
|
|
377
|
-
onDelete(event) {
|
|
378
|
-
if (!this.isFF || !window.getSelection) {
|
|
379
|
-
return
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// fix backspace bug in FF
|
|
383
|
-
// https://bugzilla.mozilla.org/show_bug.cgi?id=685445
|
|
384
|
-
const selection = window.getSelection()
|
|
385
|
-
const node = event.target
|
|
386
|
-
if (!selection.isCollapsed || !selection.rangeCount) {
|
|
387
|
-
return
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const curRange = selection.getRangeAt(selection.rangeCount - 1)
|
|
391
|
-
if (curRange.commonAncestorContainer.nodeType === 3 && curRange.startOffset > 0) {
|
|
392
|
-
// we are in child selection. The characters of the text node is being deleted
|
|
393
|
-
return
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const range = document.createRange()
|
|
397
|
-
if (selection.anchorNode !== node) {
|
|
398
|
-
// selection is in character mode. expand it to the whole editable field
|
|
399
|
-
range.selectNodeContents(node)
|
|
400
|
-
range.setEndBefore(selection.anchorNode)
|
|
401
|
-
} else if (selection.anchorOffset > 0) {
|
|
402
|
-
range.setEnd(node, selection.anchorOffset)
|
|
403
|
-
} else {
|
|
404
|
-
// reached the beginning of editable field
|
|
405
|
-
return
|
|
406
|
-
}
|
|
407
|
-
range.setStart(node, range.endOffset - 1)
|
|
408
|
-
|
|
409
|
-
const previousNode = range.cloneContents().lastChild
|
|
410
|
-
if (previousNode && previousNode.contentEditable === 'false') {
|
|
411
|
-
// this is some rich content, e.g. smile. We should help the user to delete it
|
|
412
|
-
range.deleteContents()
|
|
413
|
-
event.preventDefault()
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Enter key pressed. Submits if not multiline
|
|
419
|
-
* @param {Event} event the keydown event
|
|
420
|
-
*/
|
|
421
|
-
onEnter(event) {
|
|
422
|
-
// Prevent submitting if autocompletion menu
|
|
423
|
-
// is opened or length is over maxlength
|
|
424
|
-
if (this.multiline || this.isOverMaxlength || this.tribute.isActive) {
|
|
425
|
-
return
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
event.preventDefault()
|
|
429
|
-
event.stopPropagation()
|
|
430
|
-
this.$emit('submit', event)
|
|
431
|
-
},
|
|
432
|
-
/**
|
|
433
|
-
* Ctrl + Enter key pressed
|
|
434
|
-
* @param {Event} event the keydown event
|
|
435
|
-
*/
|
|
436
|
-
onCtrlEnter(event) {
|
|
437
|
-
if (this.isOverMaxlength) {
|
|
438
|
-
return
|
|
439
|
-
}
|
|
440
|
-
this.$emit('submit', event)
|
|
441
|
-
},
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Debounce the autocomplete function
|
|
445
|
-
*/
|
|
446
|
-
debouncedAutoComplete: debounce(async function(search, callback) {
|
|
447
|
-
this.autoComplete(search, callback)
|
|
448
|
-
}, 100),
|
|
449
|
-
},
|
|
450
|
-
}
|
|
451
|
-
</script>
|
|
452
|
-
|
|
453
|
-
<style lang="scss" scoped>
|
|
454
|
-
// Standalone styling, independent from server
|
|
455
|
-
.rich-contenteditable__input {
|
|
456
|
-
overflow-y: auto;
|
|
457
|
-
width: auto;
|
|
458
|
-
margin: 0;
|
|
459
|
-
padding: 6px;
|
|
460
|
-
cursor: text;
|
|
461
|
-
white-space: pre-wrap;
|
|
462
|
-
word-break: break-word;
|
|
463
|
-
color: var(--color-main-text);
|
|
464
|
-
border: 1px solid var(--color-border-dark);
|
|
465
|
-
border-radius: var(--border-radius);
|
|
466
|
-
outline: none;
|
|
467
|
-
background-color: var(--color-main-background);
|
|
468
|
-
font-family: var(--font-face);
|
|
469
|
-
font-size: inherit;
|
|
470
|
-
min-height: $clickable-area;
|
|
471
|
-
max-height: $clickable-area * 5.5;
|
|
472
|
-
|
|
473
|
-
// Cannot use :empty because of firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=1513303
|
|
474
|
-
&--empty:before {
|
|
475
|
-
position: absolute;
|
|
476
|
-
content: attr(placeholder);
|
|
477
|
-
color: var(--color-text-maxcontrast);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
&[contenteditable='false'] {
|
|
481
|
-
cursor: default;
|
|
482
|
-
opacity: .5;
|
|
483
|
-
color: var(--color-text-lighter);
|
|
484
|
-
border: 1px solid var(--color-background-darker);
|
|
485
|
-
border-radius: var(--border-radius);
|
|
486
|
-
background-color: var(--color-background-dark);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
&--multiline {
|
|
490
|
-
min-height: $clickable-area * 3;
|
|
491
|
-
// No max for mutiline
|
|
492
|
-
max-height: none;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
</style>
|
|
497
|
-
|
|
498
|
-
<style lang="scss">
|
|
499
|
-
@import '../../fonts/scss/iconfont-vue';
|
|
500
|
-
|
|
501
|
-
.tribute-container {
|
|
502
|
-
z-index: 9000;
|
|
503
|
-
overflow: auto;
|
|
504
|
-
min-width: 250px;
|
|
505
|
-
max-width: 300px;
|
|
506
|
-
// Show maximum 4 entries and a half to show scroll
|
|
507
|
-
// 44px + 10px padding
|
|
508
|
-
max-height: ($clickable-area + 20px) * 4.5;
|
|
509
|
-
// Space it out a bit from the text
|
|
510
|
-
margin: 5px 0;
|
|
511
|
-
color: var(--color-main-text);
|
|
512
|
-
border-radius: var(--border-radius);
|
|
513
|
-
background: var(--color-main-background);
|
|
514
|
-
box-shadow: 0 1px 5px var(--color-box-shadow);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
</style>
|