frappe-ui 0.1.203 → 0.1.205
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/package.json +1 -1
- package/src/components/Checkbox/Checkbox.vue +2 -2
- package/src/components/Dialog/Dialog.vue +10 -0
- package/src/components/Dialog/types.ts +1 -0
- package/src/components/TextEditor/LinkPopup.vue +104 -0
- package/src/components/TextEditor/link-extension.ts +21 -8
- package/src/components/TextEditor/EditLink.vue +0 -63
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="inline-flex space-x-2 rounded transition
|
|
3
|
+
class="inline-flex space-x-2 rounded transition"
|
|
4
4
|
:class="{
|
|
5
5
|
'px-2.5 py-1.5': padding && size === 'sm',
|
|
6
6
|
'px-3 py-2': padding && size === 'md',
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}"
|
|
10
10
|
>
|
|
11
11
|
<input
|
|
12
|
-
class="rounded-sm mt-[1px]"
|
|
12
|
+
class="rounded-sm mt-[1px] bg-surface-white"
|
|
13
13
|
:class="inputClasses"
|
|
14
14
|
type="checkbox"
|
|
15
15
|
:disabled="disabled"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<div
|
|
10
10
|
class="flex min-h-screen flex-col items-center px-4 py-4 text-center"
|
|
11
11
|
:class="dialogPositionClasses"
|
|
12
|
+
:style="dialogPositionStyles"
|
|
12
13
|
>
|
|
13
14
|
<DialogContent
|
|
14
15
|
class="my-8 inline-block w-full transform overflow-hidden rounded-xl bg-surface-modal text-left align-middle shadow-xl dialog-content focus-visible:outline-none"
|
|
@@ -216,6 +217,8 @@ const icon = computed(() => {
|
|
|
216
217
|
})
|
|
217
218
|
|
|
218
219
|
const dialogPositionClasses = computed(() => {
|
|
220
|
+
if (props.options?.paddingTop) return ''
|
|
221
|
+
|
|
219
222
|
const position = props.options?.position || 'center'
|
|
220
223
|
const classMap: Record<string, string> = {
|
|
221
224
|
center: 'justify-center',
|
|
@@ -224,6 +227,13 @@ const dialogPositionClasses = computed(() => {
|
|
|
224
227
|
return classMap[position]
|
|
225
228
|
})
|
|
226
229
|
|
|
230
|
+
const dialogPositionStyles = computed(() => {
|
|
231
|
+
if (props.options?.paddingTop) {
|
|
232
|
+
return { paddingTop: props.options.paddingTop }
|
|
233
|
+
}
|
|
234
|
+
return {}
|
|
235
|
+
})
|
|
236
|
+
|
|
227
237
|
const dialogIconBgClasses = computed(() => {
|
|
228
238
|
const appearance = icon.value?.appearance
|
|
229
239
|
if (!appearance) return 'bg-surface-gray-2'
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="p-2 w-72 flex items-center gap-2 bg-surface-white shadow-xl rounded"
|
|
4
|
+
>
|
|
5
|
+
<TextInput
|
|
6
|
+
v-if="edit"
|
|
7
|
+
ref="input"
|
|
8
|
+
type="text"
|
|
9
|
+
class="w-full"
|
|
10
|
+
placeholder="https://example.com"
|
|
11
|
+
v-model="_href"
|
|
12
|
+
@keydown.enter="submitLink"
|
|
13
|
+
@keydown.esc="$emit('close')"
|
|
14
|
+
/>
|
|
15
|
+
<a
|
|
16
|
+
v-else
|
|
17
|
+
class="text-ink-gray-700 underline text-sm flex-1 truncate pl-1"
|
|
18
|
+
:title="_href"
|
|
19
|
+
:href="_href"
|
|
20
|
+
target="_blank"
|
|
21
|
+
>
|
|
22
|
+
{{ _href }}
|
|
23
|
+
</a>
|
|
24
|
+
<div class="shrink-0 flex items-center gap-1.5 ml-auto">
|
|
25
|
+
<template v-if="edit">
|
|
26
|
+
<Button
|
|
27
|
+
@click="submitLink"
|
|
28
|
+
tooltip="Submit"
|
|
29
|
+
:icon="LucideCheck"
|
|
30
|
+
variant="subtle"
|
|
31
|
+
/>
|
|
32
|
+
<Button
|
|
33
|
+
@click="props.href ? (edit = false) : $emit('updateHref', '')"
|
|
34
|
+
tooltip="Exit"
|
|
35
|
+
:icon="LucideX"
|
|
36
|
+
variant="subtle"
|
|
37
|
+
/>
|
|
38
|
+
</template>
|
|
39
|
+
<template v-else>
|
|
40
|
+
<Button
|
|
41
|
+
@click="copyLink"
|
|
42
|
+
tooltip="Copy"
|
|
43
|
+
:icon="LucideCopy"
|
|
44
|
+
variant="subtle"
|
|
45
|
+
/>
|
|
46
|
+
<Button
|
|
47
|
+
@click="edit = true"
|
|
48
|
+
tooltip="Edit"
|
|
49
|
+
:icon="LucidePencil"
|
|
50
|
+
variant="subtle"
|
|
51
|
+
/>
|
|
52
|
+
<Button
|
|
53
|
+
tooltip="Remove"
|
|
54
|
+
variant="subtle"
|
|
55
|
+
@click="$emit('updateHref', '')"
|
|
56
|
+
:icon="Link2Off"
|
|
57
|
+
/>
|
|
58
|
+
</template>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<script setup lang="ts">
|
|
64
|
+
import { onMounted, ref, useTemplateRef, nextTick } from 'vue'
|
|
65
|
+
import Button from '../Button/Button.vue'
|
|
66
|
+
import TextInput from '../TextInput/TextInput.vue'
|
|
67
|
+
import LucideCopy from '~icons/lucide/copy'
|
|
68
|
+
import LucideCheck from '~icons/lucide/check'
|
|
69
|
+
import LucidePencil from '~icons/lucide/pencil'
|
|
70
|
+
import LucideX from '~icons/lucide/x'
|
|
71
|
+
import Link2Off from '~icons/lucide/link-2-off'
|
|
72
|
+
import { isValidUrl } from '../../utils/url-validation'
|
|
73
|
+
|
|
74
|
+
const props = defineProps<{
|
|
75
|
+
href: string
|
|
76
|
+
}>()
|
|
77
|
+
|
|
78
|
+
const emit = defineEmits<{
|
|
79
|
+
(e: 'updateHref', href: string): void
|
|
80
|
+
(e: 'close'): void
|
|
81
|
+
}>()
|
|
82
|
+
|
|
83
|
+
const _href = ref(props.href)
|
|
84
|
+
const input = useTemplateRef('input')
|
|
85
|
+
const edit = ref(props.href === '')
|
|
86
|
+
|
|
87
|
+
const submitLink = () => {
|
|
88
|
+
if (_href.value === '' || isValidUrl(_href.value)) {
|
|
89
|
+
emit('updateHref', _href.value)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const copyLink = async () => {
|
|
94
|
+
if (_href.value) await navigator.clipboard.writeText(_href.value)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
onMounted(async () => {
|
|
98
|
+
await nextTick()
|
|
99
|
+
if (input.value?.el) {
|
|
100
|
+
input.value.el.focus()
|
|
101
|
+
input.value.el.select()
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
</script>
|
|
@@ -4,7 +4,7 @@ import tippy, { type Instance as TippyInstance } from 'tippy.js'
|
|
|
4
4
|
import { getMarkRange, Range, Editor } from '@tiptap/core'
|
|
5
5
|
import { MarkType, Mark as ProseMirrorMark } from '@tiptap/pm/model'
|
|
6
6
|
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
|
7
|
-
import
|
|
7
|
+
import LinkPopup from './LinkPopup.vue'
|
|
8
8
|
import { linkPasteHandler } from './linkPasteHandler'
|
|
9
9
|
|
|
10
10
|
declare module '@tiptap/core' {
|
|
@@ -50,10 +50,8 @@ export const LinkExtension = Link.extend({
|
|
|
50
50
|
const markRange = getMarkRange($pos, this.type)
|
|
51
51
|
if (markRange) {
|
|
52
52
|
range = markRange
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
.marks()
|
|
56
|
-
.find((m) => m.type === this.type)
|
|
53
|
+
const node = doc.nodeAt($pos.pos)
|
|
54
|
+
if(node) mark = node.marks.find((m) => m.type === this.type)
|
|
57
55
|
|
|
58
56
|
// Select the link text
|
|
59
57
|
editor
|
|
@@ -147,7 +145,6 @@ export const LinkExtension = Link.extend({
|
|
|
147
145
|
|
|
148
146
|
addProseMirrorPlugins() {
|
|
149
147
|
let plugins = this.parent?.() || []
|
|
150
|
-
|
|
151
148
|
plugins.push(
|
|
152
149
|
linkPasteHandler({
|
|
153
150
|
editor: this.editor,
|
|
@@ -162,7 +159,23 @@ export const LinkExtension = Link.extend({
|
|
|
162
159
|
type: this.type,
|
|
163
160
|
}),
|
|
164
161
|
)
|
|
165
|
-
|
|
162
|
+
plugins.push(
|
|
163
|
+
new Plugin({
|
|
164
|
+
props: {
|
|
165
|
+
handleClick: (view, pos, event) => {
|
|
166
|
+
if (!this.editor.isEditable) return
|
|
167
|
+
if (!this.editor.isActive('link')) return false
|
|
168
|
+
event.preventDefault()
|
|
169
|
+
if (event.metaKey) {
|
|
170
|
+
const url = event.target?.getAttribute('href')
|
|
171
|
+
if (url) window.open(url, '_blank')
|
|
172
|
+
} else {
|
|
173
|
+
this.editor.commands.openLinkEditor()
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
)
|
|
166
179
|
return plugins
|
|
167
180
|
},
|
|
168
181
|
})
|
|
@@ -233,7 +246,7 @@ function openLinkEditor(href: string, anchor: HTMLElement): Promise<string> {
|
|
|
233
246
|
|
|
234
247
|
app = createApp({
|
|
235
248
|
render() {
|
|
236
|
-
return h(
|
|
249
|
+
return h(LinkPopup, {
|
|
237
250
|
href,
|
|
238
251
|
onClose: () => {
|
|
239
252
|
settlePromise('reject', 'Link editing cancelled')
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
class="p-2 flex min-w-72 items-center gap-2 bg-surface-white shadow-xl rounded-lg"
|
|
4
|
-
>
|
|
5
|
-
<TextInput
|
|
6
|
-
ref="input"
|
|
7
|
-
type="text"
|
|
8
|
-
class="w-full"
|
|
9
|
-
placeholder="https://example.com"
|
|
10
|
-
v-model="_href"
|
|
11
|
-
@keydown.enter="submitLink"
|
|
12
|
-
@keydown.esc="$emit('close')"
|
|
13
|
-
/>
|
|
14
|
-
<div class="shrink-0 flex items-center gap-2">
|
|
15
|
-
<Tooltip text="Submit" placement="top">
|
|
16
|
-
<Button label="Submit" @click="submitLink">
|
|
17
|
-
<template #icon><LucideCheck class="size-4" /></template>
|
|
18
|
-
</Button>
|
|
19
|
-
</Tooltip>
|
|
20
|
-
<Tooltip text="Remove link" placement="top">
|
|
21
|
-
<Button label="Remove link" @click="$emit('updateHref', '')">
|
|
22
|
-
<template #icon><LucideX class="size-4" /></template>
|
|
23
|
-
</Button>
|
|
24
|
-
</Tooltip>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
</template>
|
|
28
|
-
|
|
29
|
-
<script setup lang="ts">
|
|
30
|
-
import { onMounted, ref, useTemplateRef, nextTick } from 'vue'
|
|
31
|
-
import Button from '../Button/Button.vue'
|
|
32
|
-
import TextInput from '../TextInput/TextInput.vue'
|
|
33
|
-
import Tooltip from '../Tooltip/Tooltip.vue'
|
|
34
|
-
import LucideCheck from '~icons/lucide/check'
|
|
35
|
-
import LucideX from '~icons/lucide/x'
|
|
36
|
-
import { isValidUrl } from '../../utils/url-validation'
|
|
37
|
-
|
|
38
|
-
const props = defineProps<{
|
|
39
|
-
href: string
|
|
40
|
-
}>()
|
|
41
|
-
|
|
42
|
-
const emit = defineEmits<{
|
|
43
|
-
(e: 'updateHref', href: string): void
|
|
44
|
-
(e: 'close'): void
|
|
45
|
-
}>()
|
|
46
|
-
|
|
47
|
-
const _href = ref(props.href)
|
|
48
|
-
const input = useTemplateRef('input')
|
|
49
|
-
|
|
50
|
-
const submitLink = () => {
|
|
51
|
-
if (_href.value === '' || isValidUrl(_href.value)) {
|
|
52
|
-
emit('updateHref', _href.value)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
onMounted(async () => {
|
|
57
|
-
await nextTick()
|
|
58
|
-
if (input.value?.el) {
|
|
59
|
-
input.value.el.focus()
|
|
60
|
-
input.value.el.select()
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
</script>
|