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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frappe-ui",
3
- "version": "0.1.203",
3
+ "version": "0.1.205",
4
4
  "description": "A set of components and utilities for rapid UI development",
5
5
  "main": "./src/index.ts",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- class="inline-flex space-x-2 rounded transition bg-surface-white"
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'
@@ -25,6 +25,7 @@ type DialogOptions = {
25
25
  actions?: Array<DialogAction>
26
26
  // default position = 'center'
27
27
  position?: 'top' | 'center'
28
+ paddingTop?: string | number
28
29
  }
29
30
 
30
31
  export type DialogActionContext = {
@@ -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 EditLink from './EditLink.vue'
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
- mark = doc
54
- .resolve(markRange.from)
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(EditLink, {
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>