@leaflink/stash 50.4.0 → 50.5.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.
@@ -1 +1 @@
1
- {"version":3,"file":"Textarea.js","sources":["../src/components/Textarea/Textarea.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, nextTick, onBeforeUnmount, onMounted, ref, useAttrs, useCssModule, useSlots, watch } from 'vue';\n\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n\n export interface TextareaResizeOptions {\n /**\n * It will automatically scroll the page down when it reaches the bottom of the viewport/element\n */\n forceBrowserScroll: boolean;\n }\n\n export interface TextAreaProps extends FieldProps {\n /**\n * Value for the textarea element.\n */\n modelValue?: string;\n\n /**\n * Deprecated. Use :model-value or v-model instead of :value.\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: string | number | null;\n\n /**\n * Allow textarea to be resizable vertically.\n * Alternatively if you want to disable automatic scroll when resizing, you can set `{ forceBrowserScroll: false }`\n */\n resize?: boolean | TextareaResizeOptions;\n\n /**\n * Placeholder text for the textarea element.\n * **Note:** placeholders should be used to display examples; they should not be used as labels because they are not accessible as labels. If a real label cannot be used, use the `aria-label` attribute.\n */\n placeholder?: string;\n }\n\n defineOptions({\n name: 'll-textarea',\n });\n\n const attrs = useAttrs();\n const slots = useSlots();\n const classes = useCssModule();\n\n const props = withDefaults(defineProps<TextAreaProps>(), {\n modelValue: '',\n value: null,\n resize: false,\n placeholder: undefined,\n });\n\n const emits = defineEmits<{\n /**\n * Emitted when the model value changes.\n */\n (e: 'update:model-value', value: string): void;\n }>();\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const observer = ref<ResizeObserver>();\n const isReadOnly = computed(() => props.isReadOnly || ('readonly' in attrs && attrs.readonly !== false));\n\n const inputAttrs = computed(() => {\n const allAttrs = { ...attrs };\n\n delete allAttrs['data-test'];\n delete allAttrs.class;\n\n return allAttrs;\n });\n\n watch(\n () => props.resize,\n (v) => {\n v ? setupResizeObserver() : observer.value?.disconnect();\n },\n );\n\n const onInput = (event: Event) => {\n emits('update:model-value', (event.target as HTMLTextAreaElement).value);\n };\n\n const setupResizeObserver = () => {\n if (observer.value || !textareaRef.value) {\n return;\n }\n\n // the ResizeObserver will be in charge to detect if page needs to scroll when resizing the component\n observer.value = new ResizeObserver(([entry]) => {\n const { target } = entry;\n const parent = findParentScrollable(textareaRef.value as HTMLTextAreaElement) || document.documentElement;\n\n const { scrollTop: scrollPosition } = parent;\n let offsetDiff = 0;\n\n // checks if the closest parent element scrollable is the document page\n if (parent === document.documentElement) {\n const { top, height } = getOffsetClipRect(target as HTMLElement);\n const { innerHeight: viewportHeight } = window;\n\n offsetDiff = Math.max(top + height - (viewportHeight + scrollPosition), 0);\n } else {\n const { top, height } = (target as HTMLElement).getBoundingClientRect();\n const { top: parentTop } = parent.getBoundingClientRect();\n const { offsetHeight: parentHeight } = parent;\n const offsetTop = top - parentTop;\n\n offsetDiff = Math.max(offsetTop + height - parentHeight, 0);\n }\n\n if (offsetDiff) {\n requestAnimationFrame(() => {\n parent.scrollTop = scrollPosition + offsetDiff;\n });\n }\n });\n\n observer.value.observe(textareaRef.value);\n };\n\n /**\n * Retrieve the closest parent that has a scroll. Defaults to the document page.\n */\n const findParentScrollable = (el: HTMLElement): HTMLElement | null => {\n const parent = el.parentElement as HTMLElement;\n if (!parent) {\n return null;\n }\n\n const { overflowY } = getComputedStyle(parent);\n if (overflowY !== 'visible') {\n return parent;\n }\n\n if (parent === document.body) {\n return document.documentElement;\n }\n\n return findParentScrollable(parent);\n };\n\n /**\n * Retrieve element absolute positioning relative to the page.\n */\n const getOffsetClipRect = (el: HTMLElement) => {\n const { offsetWidth: width, offsetHeight: height } = el;\n\n let left = 0;\n let top = 0;\n\n const findPos = function ({ offsetLeft, offsetTop, offsetParent }: HTMLElement) {\n left += offsetLeft;\n top += offsetTop;\n\n if (offsetParent) {\n findPos(offsetParent as HTMLElement);\n }\n };\n\n findPos(el);\n\n return {\n top,\n left,\n width,\n height,\n };\n };\n\n onMounted(async () => {\n if (props.value !== null) {\n throw new Error('ll-input: use :model-value or v-model instead of :value.');\n }\n\n if (attrs.onInput) {\n throw new Error('ll-input: use the @update:model-value event instead of @input');\n }\n\n if (\n (typeof props.resize === 'boolean' && props.resize) ||\n (props.resize as TextareaResizeOptions)?.forceBrowserScroll\n ) {\n await nextTick();\n setupResizeObserver();\n }\n });\n\n onBeforeUnmount(() => {\n observer.value?.disconnect();\n });\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-textarea\" :class=\"[classes.root]\" data-test=\"stash-textarea\">\n <template #default=\"{ fieldId, hasError }\">\n <textarea\n :id=\"fieldId\"\n ref=\"textareaRef\"\n :class=\"[\n classes.textarea,\n { 'stash-textarea--error': hasError, 'tw-resize-y': props.resize, 'tw-resize-none': !props.resize },\n ]\"\n :value=\"props.modelValue\"\n data-test=\"stash-textarea|textarea\"\n :placeholder=\"props.placeholder\"\n v-bind=\"inputAttrs\"\n :disabled=\"props.disabled\"\n :readonly=\"isReadOnly\"\n @input=\"onInput\"\n ></textarea>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <!-- @slot Hint content -->\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style module>\n .root {\n position: relative;\n width: 100%;\n }\n\n .textarea {\n background: var(--color-white);\n border: 1px solid;\n border-color: var(--color-ice-500);\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n min-height: 100px;\n outline: none;\n padding: theme('spacing[1.5]');\n width: 100%;\n\n &:hover {\n border-color: var(--color-ice-500);\n }\n\n &:focus,\n &:active {\n border-color: var(--color-blue-500);\n }\n\n &.stash-textarea--error {\n border-color: var(--color-red-500);\n color: var(--color-red-500);\n }\n\n &::placeholder {\n color: var(--color-ice-500);\n opacity: 1;\n }\n\n &[disabled] {\n background-color: var(--color-ice-100);\n border-color: var(--color-ice-500);\n color: var(--color-ice-500);\n pointer-events: none;\n }\n\n &[disabled]:active,\n &[disabled]:focus {\n box-shadow: none;\n }\n\n &[disabled]::placeholder {\n text-transform: none;\n color: var(--color-ice-500);\n }\n\n &[readonly] {\n border-color: transparent;\n background-color: transparent;\n padding-left: 0;\n padding-right: 0;\n min-height: unset;\n }\n }\n</style>\n"],"names":["attrs","useAttrs","slots","useSlots","classes","useCssModule","props","__props","emits","__emit","textareaRef","ref","observer","isReadOnly","computed","inputAttrs","allAttrs","watch","v","setupResizeObserver","_a","onInput","event","entry","target","parent","findParentScrollable","scrollPosition","offsetDiff","top","height","getOffsetClipRect","viewportHeight","parentTop","parentHeight","offsetTop","el","overflowY","width","left","findPos","offsetLeft","offsetParent","onMounted","nextTick","onBeforeUnmount"],"mappings":"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"}
1
+ {"version":3,"file":"Textarea.js","sources":["../src/components/Textarea/Textarea.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, nextTick, onBeforeUnmount, onMounted, ref, useAttrs, useCssModule, useSlots, watch } from 'vue';\n\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n\n export interface TextareaResizeOptions {\n /**\n * It will automatically scroll the page down when it reaches the bottom of the viewport/element\n */\n forceBrowserScroll: boolean;\n }\n\n export interface TextAreaProps extends FieldProps {\n /**\n * Value for the textarea element.\n */\n modelValue?: string;\n\n /**\n * Deprecated. Use :model-value or v-model instead of :value.\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: string | number | null;\n\n /**\n * Allow textarea to be resizable vertically.\n * Alternatively if you want to disable automatic scroll when resizing, you can set `{ forceBrowserScroll: false }`\n */\n resize?: boolean | TextareaResizeOptions;\n\n /**\n * Placeholder text for the textarea element.\n * **Note:** placeholders should be used to display examples; they should not be used as labels because they are not accessible as labels. If a real label cannot be used, use the `aria-label` attribute.\n */\n placeholder?: string;\n }\n\n defineOptions({\n name: 'll-textarea',\n });\n\n const attrs = useAttrs();\n const slots = useSlots();\n const classes = useCssModule();\n\n const props = withDefaults(defineProps<TextAreaProps>(), {\n modelValue: '',\n value: null,\n resize: false,\n placeholder: undefined,\n });\n\n const emits = defineEmits<{\n /**\n * Emitted when the model value changes.\n */\n (e: 'update:model-value', value: string): void;\n }>();\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const observer = ref<ResizeObserver>();\n const isReadOnly = computed(() => props.isReadOnly || ('readonly' in attrs && attrs.readonly !== false));\n\n const inputAttrs = computed(() => {\n const allAttrs = { ...attrs };\n\n delete allAttrs['data-test'];\n delete allAttrs.class;\n\n return allAttrs;\n });\n\n watch(\n () => props.resize,\n (v) => {\n v ? setupResizeObserver() : observer.value?.disconnect();\n },\n );\n\n const onInput = (event: Event) => {\n emits('update:model-value', (event.target as HTMLTextAreaElement).value);\n };\n\n const setupResizeObserver = () => {\n if (observer.value || !textareaRef.value) {\n return;\n }\n\n // the ResizeObserver will be in charge to detect if page needs to scroll when resizing the component\n observer.value = new ResizeObserver(([entry]) => {\n const { target } = entry;\n const parent = findParentScrollable(textareaRef.value as HTMLTextAreaElement) || document.documentElement;\n\n const { scrollTop: scrollPosition } = parent;\n let offsetDiff = 0;\n\n // checks if the closest parent element scrollable is the document page\n if (parent === document.documentElement) {\n const { top, height } = getOffsetClipRect(target as HTMLElement);\n const { innerHeight: viewportHeight } = window;\n\n offsetDiff = Math.max(top + height - (viewportHeight + scrollPosition), 0);\n } else {\n const { top, height } = (target as HTMLElement).getBoundingClientRect();\n const { top: parentTop } = parent.getBoundingClientRect();\n const { offsetHeight: parentHeight } = parent;\n const offsetTop = top - parentTop;\n\n offsetDiff = Math.max(offsetTop + height - parentHeight, 0);\n }\n\n if (offsetDiff) {\n requestAnimationFrame(() => {\n parent.scrollTop = scrollPosition + offsetDiff;\n });\n }\n });\n\n observer.value.observe(textareaRef.value);\n };\n\n /**\n * Retrieve the closest parent that has a scroll. Defaults to the document page.\n */\n const findParentScrollable = (el: HTMLElement): HTMLElement | null => {\n const parent = el.parentElement as HTMLElement;\n if (!parent) {\n return null;\n }\n\n const { overflowY } = getComputedStyle(parent);\n if (overflowY !== 'visible') {\n return parent;\n }\n\n if (parent === document.body) {\n return document.documentElement;\n }\n\n return findParentScrollable(parent);\n };\n\n /**\n * Retrieve element absolute positioning relative to the page.\n */\n const getOffsetClipRect = (el: HTMLElement) => {\n const { offsetWidth: width, offsetHeight: height } = el;\n\n let left = 0;\n let top = 0;\n\n const findPos = function ({ offsetLeft, offsetTop, offsetParent }: HTMLElement) {\n left += offsetLeft;\n top += offsetTop;\n\n if (offsetParent) {\n findPos(offsetParent as HTMLElement);\n }\n };\n\n findPos(el);\n\n return {\n top,\n left,\n width,\n height,\n };\n };\n\n onMounted(async () => {\n if (props.value !== null) {\n throw new Error('ll-input: use :model-value or v-model instead of :value.');\n }\n\n if (attrs.onInput) {\n throw new Error('ll-input: use the @update:model-value event instead of @input');\n }\n\n if (\n (typeof props.resize === 'boolean' && props.resize) ||\n (props.resize as TextareaResizeOptions)?.forceBrowserScroll\n ) {\n await nextTick();\n setupResizeObserver();\n }\n });\n\n onBeforeUnmount(() => {\n observer.value?.disconnect();\n });\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-textarea\" :class=\"[classes.root]\" data-test=\"stash-textarea\">\n <template #default=\"{ fieldId, hasError }\">\n <textarea\n :id=\"fieldId\"\n ref=\"textareaRef\"\n :class=\"[\n classes.textarea,\n 'tw-border tw-border-ice-500',\n {\n 'stash-textarea--error tw-border-red-500 tw-text-red-500': hasError,\n 'tw-text-ice-700 hover:tw-border-ice-500 focus:tw-border-blue-500 active:tw-border-blue-500':\n !hasError && !props.disabled,\n 'tw-resize-y': props.resize,\n 'tw-resize-none': !props.resize,\n },\n ]\"\n :value=\"props.modelValue\"\n data-test=\"stash-textarea|textarea\"\n :placeholder=\"props.placeholder\"\n v-bind=\"inputAttrs\"\n :disabled=\"props.disabled\"\n :readonly=\"isReadOnly\"\n @input=\"onInput\"\n ></textarea>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <!-- @slot Hint content -->\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style module>\n .root {\n position: relative;\n width: 100%;\n }\n\n .textarea {\n background: var(--color-white);\n border-radius: theme('borderRadius.DEFAULT');\n display: block;\n min-height: 100px;\n outline: none;\n padding: theme('spacing[1.5]');\n width: 100%;\n\n &::placeholder {\n color: var(--color-ice-500);\n opacity: 1;\n }\n\n &[disabled] {\n background-color: var(--color-ice-100);\n border-color: var(--color-ice-500);\n color: var(--color-ice-500);\n pointer-events: none;\n }\n\n &[disabled]:active,\n &[disabled]:focus {\n box-shadow: none;\n }\n\n &[disabled]::placeholder {\n text-transform: none;\n color: var(--color-ice-500);\n }\n\n &[readonly] {\n border-color: transparent;\n background-color: transparent;\n padding-left: 0;\n padding-right: 0;\n min-height: unset;\n }\n }\n</style>\n"],"names":["attrs","useAttrs","slots","useSlots","classes","useCssModule","props","__props","emits","__emit","textareaRef","ref","observer","isReadOnly","computed","inputAttrs","allAttrs","watch","v","setupResizeObserver","_a","onInput","event","entry","target","parent","findParentScrollable","scrollPosition","offsetDiff","top","height","getOffsetClipRect","viewportHeight","parentTop","parentHeight","offsetTop","el","overflowY","width","left","findPos","offsetLeft","offsetParent","onMounted","nextTick","onBeforeUnmount"],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import { defineComponent as w, useCssModule as q, inject as C, computed as T, toValue as x, openBlock as b, createElementBlock as B, normalizeClass as s, unref as t, createVNode as h, createBlock as I, withModifiers as y, withCtx as M, createCommentVNode as N, renderSlot as $ } from "vue";
2
+ import { t as V } from "./locale.js";
3
+ import z from "./Button.js";
4
+ import E from "./Icon.js";
5
+ import { _ as U } from "./Image.vue_vue_type_script_setup_true_lang-YUNunj71.js";
6
+ import { T as d } from "./ThumbnailGroup.keys-D6WL5xQ5.js";
7
+ import { _ as j } from "./_plugin-vue_export-helper-CHgC5LLL.js";
8
+ const A = ["aria-current", "draggable"], D = /* @__PURE__ */ w({
9
+ __name: "Thumbnail",
10
+ props: {
11
+ thumbnail: {},
12
+ altText: { default: "" },
13
+ variant: { default: "opaque" }
14
+ },
15
+ emits: ["remove", "click"],
16
+ setup(_, { emit: f }) {
17
+ const u = _, r = f, a = q(), { thumbnails: v, activeThumbnail: n, draggable: p, isDragging: m, radius: i, removable: o } = C(
18
+ d.key,
19
+ d.defaults
20
+ ), e = T(() => x(v).findIndex((l) => l === u.thumbnail));
21
+ function g() {
22
+ n.value = e.value, r("click", e.value);
23
+ }
24
+ function k() {
25
+ r("remove", e.value), n.value = -1;
26
+ }
27
+ return (l, c) => (b(), B("li", {
28
+ class: s(["stash-thumbnail tw-group tw-relative tw-cursor-pointer focus-visible:tw-outline-none", [
29
+ {
30
+ [t(a)["thumbnail-translucent"]]: u.variant === "translucent",
31
+ [t(a)["thumbnail-opaque"]]: u.variant === "opaque",
32
+ [t(a)["thumbnail-removable"]]: t(o),
33
+ [t(a)["thumbnail-circle"]]: t(i) === "circle",
34
+ [t(a)["thumbnail-rounded"]]: t(i) === "rounded"
35
+ }
36
+ ]]),
37
+ "aria-current": t(n) === e.value,
38
+ draggable: t(p),
39
+ "data-test": "stash-thumbnail",
40
+ tabindex: "0",
41
+ onClick: c[0] || (c[0] = (L) => g())
42
+ }, [
43
+ h(U, {
44
+ class: s(["stash-thumbnail__img tw-border-ice-200 tw-outline-blue-500/15 tw-transition-colors", [
45
+ t(a)["thumbnail-img"],
46
+ {
47
+ [t(a)["thumbnail-mask"]]: t(o) && t(i) !== "circle",
48
+ [t(a)["thumbnail-active"]]: t(n) === e.value && !t(m),
49
+ "group-hover:tw-border-blue-500 group-focus-visible:tw-border-blue-500 group-focus-visible:tw-outline": !t(m)
50
+ }
51
+ ]]),
52
+ "data-test": "stash-thumbnail|img",
53
+ radius: t(i),
54
+ src: l.thumbnail.imageUrl,
55
+ alt: l.altText
56
+ }, null, 8, ["class", "radius", "src", "alt"]),
57
+ t(o) ? (b(), I(z, {
58
+ key: 0,
59
+ class: s(["stash-thumbnail__remove", [t(a)["thumbnail-remove"]]]),
60
+ "data-test": "stash-thumbnail|remove",
61
+ "aria-label": t(V)("ll.thumbnail.removeButton", { index: e.value + 1 }),
62
+ type: "button",
63
+ icon: "",
64
+ onClick: y(k, ["stop", "prevent"])
65
+ }, {
66
+ default: M(() => [
67
+ h(E, {
68
+ name: "close",
69
+ size: "small"
70
+ })
71
+ ]),
72
+ _: 1
73
+ }, 8, ["class", "aria-label"])) : N("", !0),
74
+ $(l.$slots, "hint")
75
+ ], 10, A));
76
+ }
77
+ }), H = {
78
+ "thumbnail-remove": "_thumbnail-remove_afa4q_2",
79
+ "thumbnail-img": "_thumbnail-img_afa4q_24",
80
+ "thumbnail-translucent": "_thumbnail-translucent_afa4q_32",
81
+ "thumbnail-active": "_thumbnail-active_afa4q_36",
82
+ "thumbnail-mask": "_thumbnail-mask_afa4q_46",
83
+ "thumbnail-full": "_thumbnail-full_afa4q_50",
84
+ "thumbnail-rounded": "_thumbnail-rounded_afa4q_54"
85
+ }, J = {
86
+ $style: H
87
+ }, Q = /* @__PURE__ */ j(D, [["__cssModules", J]]);
88
+ export {
89
+ Q as default
90
+ };
91
+ //# sourceMappingURL=Thumbnail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Thumbnail.js","sources":["../src/components/Thumbnail/Thumbnail.vue"],"sourcesContent":["<script lang=\"ts\">\n export * from './Thumbnail.types';\n</script>\n\n<script setup lang=\"ts\" generic=\"ThumbnailItem extends ThumbnailDefinition\">\n import { computed, inject, toValue, useCssModule } from 'vue';\n\n import { t } from '../../locale';\n import Button from '../Button/Button.vue';\n import Icon from '../Icon/Icon.vue';\n import Image from '../Image/Image.vue';\n import { THUMBNAIL_INJECTION } from '../ThumbnailGroup/ThumbnailGroup.keys';\n import { ThumbnailDefinition, ThumbnailVariant } from './Thumbnail.types';\n\n interface ThumbnailProps {\n /**\n * Thumbnail data containing and id and the image url\n */\n thumbnail: ThumbnailItem;\n\n /**\n * Thumbnail alt text for the image\n * @default ''\n */\n altText?: string;\n\n /**\n * Whether the thumbnail has some transparency or is fully opaque\n * @default 'opaque'\n */\n variant?: ThumbnailVariant;\n }\n\n const props = withDefaults(defineProps<ThumbnailProps>(), {\n variant: 'opaque',\n altText: '',\n });\n\n const emit = defineEmits<{\n /**\n * Emits when a thumbnail is removed\n */\n remove: [index: number];\n\n /**\n * Emits when a thumbnail is clicked\n */\n click: [index: number];\n }>();\n\n const classes = useCssModule();\n\n const { thumbnails, activeThumbnail, draggable, isDragging, radius, removable } = inject(\n THUMBNAIL_INJECTION.key,\n THUMBNAIL_INJECTION.defaults,\n );\n\n const thumbnailIndex = computed(() => toValue(thumbnails).findIndex((t) => t === props.thumbnail));\n\n function handleThumbnailClick() {\n activeThumbnail.value = thumbnailIndex.value;\n emit('click', thumbnailIndex.value);\n }\n\n function handleRemove() {\n emit('remove', thumbnailIndex.value);\n activeThumbnail.value = -1;\n }\n</script>\n\n<template>\n <li\n class=\"stash-thumbnail tw-group tw-relative tw-cursor-pointer focus-visible:tw-outline-none\"\n :class=\"[\n {\n [classes['thumbnail-translucent']]: props.variant === 'translucent',\n [classes['thumbnail-opaque']]: props.variant === 'opaque',\n [classes['thumbnail-removable']]: removable,\n [classes['thumbnail-circle']]: radius === 'circle',\n [classes['thumbnail-rounded']]: radius === 'rounded',\n },\n ]\"\n :aria-current=\"activeThumbnail === thumbnailIndex\"\n :draggable=\"draggable\"\n data-test=\"stash-thumbnail\"\n tabindex=\"0\"\n @click=\"handleThumbnailClick()\"\n >\n <Image\n class=\"stash-thumbnail__img tw-border-ice-200 tw-outline-blue-500/15 tw-transition-colors\"\n :class=\"[\n classes['thumbnail-img'],\n {\n [classes['thumbnail-mask']]: removable && radius !== 'circle',\n [classes['thumbnail-active']]: activeThumbnail === thumbnailIndex && !isDragging,\n 'group-hover:tw-border-blue-500 group-focus-visible:tw-border-blue-500 group-focus-visible:tw-outline':\n !isDragging,\n },\n ]\"\n data-test=\"stash-thumbnail|img\"\n :radius=\"radius\"\n :src=\"thumbnail.imageUrl\"\n :alt=\"altText\"\n />\n\n <Button\n v-if=\"removable\"\n class=\"stash-thumbnail__remove\"\n :class=\"[classes['thumbnail-remove']]\"\n data-test=\"stash-thumbnail|remove\"\n :aria-label=\"t('ll.thumbnail.removeButton', { index: thumbnailIndex + 1 })\"\n type=\"button\"\n icon\n @click.stop.prevent=\"handleRemove\"\n >\n <Icon name=\"close\" size=\"small\" />\n </Button>\n\n <!-- @slot: Slot to add a hint below the image -->\n <slot name=\"hint\" />\n </li>\n</template>\n\n<style module>\n .thumbnail-remove {\n position: absolute;\n top: calc(theme('spacing[1.5]') * -1);\n right: calc(theme('spacing[1.5]') * -1);\n padding: 1px;\n height: auto;\n width: auto !important;\n min-width: auto;\n border-radius: theme('borderRadius.full');\n background: var(--color-blue-500);\n color: white;\n\n &:hover,\n &:focus {\n background: var(--color-blue-600);\n }\n\n &:focus {\n outline: none;\n }\n }\n\n .thumbnail-img {\n border-style: solid;\n object-fit: cover;\n height: theme('spacing.12');\n width: theme('spacing.12');\n border-width: 1px;\n outline-width: theme('borderSpacing.1');\n\n .thumbnail-translucent & {\n opacity: theme('opacity.40');\n\n &:hover,\n &.thumbnail-active {\n opacity: theme('opacity.100');\n }\n }\n }\n\n .thumbnail-active {\n border: 1px solid var(--color-ice-500);\n }\n\n .thumbnail-mask {\n mask-image: radial-gradient(circle at top right, transparent 20%, white 20.5%);\n }\n\n .thumbnail-full {\n border-radius: theme('borderRadius.full');\n }\n\n .thumbnail-rounded {\n border-radius: theme('borderRadius.DEFAULT');\n }\n</style>\n"],"names":["props","__props","emit","__emit","classes","useCssModule","thumbnails","activeThumbnail","draggable","isDragging","radius","removable","inject","THUMBNAIL_INJECTION","thumbnailIndex","computed","toValue","t","handleThumbnailClick","handleRemove"],"mappings":";;;;;;;;;;;;;;;;AAiCE,UAAMA,IAAQC,GAKRC,IAAOC,GAYPC,IAAUC,KAEV,EAAE,YAAAC,GAAY,iBAAAC,GAAiB,WAAAC,GAAW,YAAAC,GAAY,QAAAC,GAAQ,WAAAC,MAAcC;AAAA,MAChFC,EAAoB;AAAA,MACpBA,EAAoB;AAAA,IAAA,GAGhBC,IAAiBC,EAAS,MAAMC,EAAQV,CAAU,EAAE,UAAU,CAACW,MAAMA,MAAMjB,EAAM,SAAS,CAAC;AAEjG,aAASkB,IAAuB;AAC9B,MAAAX,EAAgB,QAAQO,EAAe,OAClCZ,EAAA,SAASY,EAAe,KAAK;AAAA,IACpC;AAEA,aAASK,IAAe;AACjB,MAAAjB,EAAA,UAAUY,EAAe,KAAK,GACnCP,EAAgB,QAAQ;AAAA,IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,63 @@
1
+ import { AllowedComponentProps } from 'vue';
2
+ import { ComponentCustomProps } from 'vue';
3
+ import { RendererElement } from 'vue';
4
+ import { RendererNode } from 'vue';
5
+ import { ShallowUnwrapRef } from 'vue';
6
+ import { VNode } from 'vue';
7
+ import { VNodeProps } from 'vue';
8
+
9
+ declare const _default: <ThumbnailItem extends ThumbnailDefinition>(__VLS_props: {
10
+ readonly onClick?: ((index: number) => any) | undefined;
11
+ readonly onRemove?: ((index: number) => any) | undefined;
12
+ variant?: ThumbnailVariant | undefined;
13
+ thumbnail: ThumbnailItem;
14
+ altText?: string | undefined;
15
+ } & VNodeProps & AllowedComponentProps & ComponentCustomProps, __VLS_ctx?: {
16
+ attrs: any;
17
+ slots: {
18
+ hint?(_: {}): any;
19
+ };
20
+ emit: ((evt: "click", index: number) => void) & ((evt: "remove", index: number) => void);
21
+ } | undefined, __VLS_expose?: ((exposed: ShallowUnwrapRef< {}>) => void) | undefined, __VLS_setup?: Promise<{
22
+ props: {
23
+ readonly onClick?: ((index: number) => any) | undefined;
24
+ readonly onRemove?: ((index: number) => any) | undefined;
25
+ variant?: ThumbnailVariant | undefined;
26
+ thumbnail: ThumbnailItem;
27
+ altText?: string | undefined;
28
+ } & VNodeProps & AllowedComponentProps & ComponentCustomProps;
29
+ expose(exposed: ShallowUnwrapRef< {}>): void;
30
+ attrs: any;
31
+ slots: {
32
+ hint?(_: {}): any;
33
+ };
34
+ emit: ((evt: "click", index: number) => void) & ((evt: "remove", index: number) => void);
35
+ }>) => VNode<RendererNode, RendererElement, {
36
+ [key: string]: any;
37
+ }> & {
38
+ __ctx?: {
39
+ props: {
40
+ readonly onClick?: ((index: number) => any) | undefined;
41
+ readonly onRemove?: ((index: number) => any) | undefined;
42
+ variant?: ThumbnailVariant | undefined;
43
+ thumbnail: ThumbnailItem;
44
+ altText?: string | undefined;
45
+ } & VNodeProps & AllowedComponentProps & ComponentCustomProps;
46
+ expose(exposed: ShallowUnwrapRef< {}>): void;
47
+ attrs: any;
48
+ slots: {
49
+ hint?(_: {}): any;
50
+ };
51
+ emit: ((evt: "click", index: number) => void) & ((evt: "remove", index: number) => void);
52
+ } | undefined;
53
+ };
54
+ export default _default;
55
+
56
+ export declare interface ThumbnailDefinition {
57
+ id: number | string;
58
+ imageUrl: string;
59
+ }
60
+
61
+ export declare type ThumbnailVariant = 'opaque' | 'translucent';
62
+
63
+ export { }
@@ -0,0 +1,33 @@
1
+ import { defineComponent as n, useCssModule as o, inject as m, openBlock as l, createElementBlock as c, normalizeClass as r, unref as t, createVNode as u } from "vue";
2
+ import i from "./Icon.js";
3
+ import { T as s } from "./ThumbnailGroup.keys-D6WL5xQ5.js";
4
+ import { _ as p } from "./_plugin-vue_export-helper-CHgC5LLL.js";
5
+ const _ = /* @__PURE__ */ n({
6
+ __name: "ThumbnailEmpty",
7
+ setup(f) {
8
+ const a = o(), { radius: e } = m(s.key, s.defaults);
9
+ return (y, b) => (l(), c("li", {
10
+ class: r(["stash-thumbnail-empty", [
11
+ t(a)["thumbnail-empty"],
12
+ {
13
+ "tw-rounded-full": t(e) === "circle",
14
+ "tw-rounded": t(e) === "rounded"
15
+ }
16
+ ]]),
17
+ "data-test": "stash-thumbnail-empty"
18
+ }, [
19
+ u(i, {
20
+ name: "image",
21
+ "data-test": "stash-thumbnail-empty|icon"
22
+ })
23
+ ], 2));
24
+ }
25
+ }), d = {
26
+ "thumbnail-empty": "_thumbnail-empty_1c6pl_2"
27
+ }, h = {
28
+ $style: d
29
+ }, M = /* @__PURE__ */ p(_, [["__cssModules", h]]);
30
+ export {
31
+ M as default
32
+ };
33
+ //# sourceMappingURL=ThumbnailEmpty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThumbnailEmpty.js","sources":["../src/components/ThumbnailEmpty/ThumbnailEmpty.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import Icon from '../Icon/Icon.vue';\n import { THUMBNAIL_INJECTION } from '../ThumbnailGroup/ThumbnailGroup.keys';\n\n const classes = useCssModule();\n\n const { radius } = inject(THUMBNAIL_INJECTION.key, THUMBNAIL_INJECTION.defaults);\n</script>\n\n<template>\n <li\n class=\"stash-thumbnail-empty\"\n :class=\"[\n classes['thumbnail-empty'],\n {\n 'tw-rounded-full': radius === 'circle',\n 'tw-rounded': radius === 'rounded',\n },\n ]\"\n data-test=\"stash-thumbnail-empty\"\n >\n <Icon name=\"image\" data-test=\"stash-thumbnail-empty|icon\" />\n </li>\n</template>\n\n<style module>\n .thumbnail-empty {\n height: theme('spacing.12');\n width: theme('spacing.12');\n border-width: 1px;\n outline-width: theme('borderSpacing.1');\n border-style: dashed;\n border-color: var(--colors-ice-500);\n background-color: var(--colors-ice-100);\n color: var(--colors-ice-700);\n display: flex;\n justify-content: center;\n align-items: center;\n }\n</style>\n"],"names":["classes","useCssModule","radius","inject","THUMBNAIL_INJECTION"],"mappings":";;;;;;;AAME,UAAMA,IAAUC,KAEV,EAAE,QAAAC,EAAO,IAAIC,EAAOC,EAAoB,KAAKA,EAAoB,QAAQ;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,9 @@
1
+ import { ComponentOptionsMixin } from 'vue';
2
+ import { ComponentProvideOptions } from 'vue';
3
+ import { DefineComponent } from 'vue';
4
+ import { PublicProps } from 'vue';
5
+
6
+ declare const _default: DefineComponent< {}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, ComponentProvideOptions, true, {}, any>;
7
+ export default _default;
8
+
9
+ export { }
@@ -0,0 +1,73 @@
1
+ import { defineComponent as h, mergeModels as m, useModel as c, computed as l, ref as g, provide as v, openBlock as o, createElementBlock as d, renderSlot as T, normalizeProps as y, guardReactiveProps as _, unref as E, Fragment as x, renderList as w, createBlock as I } from "vue";
2
+ import B, { moveSortableElements as k } from "./useSortable.js";
3
+ import S from "./ThumbnailEmpty.js";
4
+ import { T as C } from "./ThumbnailGroup.keys-D6WL5xQ5.js";
5
+ import { _ as D } from "./_plugin-vue_export-helper-CHgC5LLL.js";
6
+ const M = /* @__PURE__ */ h({
7
+ __name: "ThumbnailGroup",
8
+ props: /* @__PURE__ */ m({
9
+ draggable: { type: Boolean, default: !1 },
10
+ radius: { default: "rounded" },
11
+ removable: { type: Boolean, default: !1 },
12
+ showEmpty: { type: Boolean, default: !1 },
13
+ emptyOptions: { default: () => ({ max: 6 }) },
14
+ thumbnails: { default: () => [] }
15
+ }, {
16
+ activeThumbnail: {
17
+ default: -1
18
+ },
19
+ activeThumbnailModifiers: {}
20
+ }),
21
+ emits: /* @__PURE__ */ m(["dragStart", "dragEnd", "update:thumbnails"], ["update:activeThumbnail"]),
22
+ setup(s, { emit: p }) {
23
+ const n = p, t = s, r = c(s, "activeThumbnail"), a = l({
24
+ get() {
25
+ return t.thumbnails;
26
+ },
27
+ set(e) {
28
+ n("update:thumbnails", e);
29
+ }
30
+ }), u = g(null), { isDragging: i } = B({
31
+ ref: u,
32
+ list: a,
33
+ isEnabled: t.draggable,
34
+ ghostClass: "thumbnail-ghost",
35
+ chosenClass: "thumbnail-chosen",
36
+ sortInPlace: !1,
37
+ onDragStart: (e) => n("dragStart", e),
38
+ onDragEnd: (e) => {
39
+ r.value = e.newIndex, k({ list: a, from: e.oldIndex, to: e.newIndex }), n("dragEnd", e);
40
+ }
41
+ }), f = l(() => {
42
+ if (!t.showEmpty) return 0;
43
+ const { max: e } = t.emptyOptions || {};
44
+ return a.value.length < e ? e - a.value.length : 0;
45
+ });
46
+ return v(
47
+ C.key,
48
+ Object.freeze({
49
+ draggable: l(() => t.draggable),
50
+ removable: l(() => t.removable),
51
+ thumbnails: a,
52
+ radius: t.radius,
53
+ activeThumbnail: r,
54
+ isDragging: i
55
+ })
56
+ ), (e, O) => (o(), d("ul", {
57
+ ref_key: "thumbnailRef",
58
+ ref: u,
59
+ class: "stash-thumbnail-group tw-flex tw-flex-wrap tw-gap-3",
60
+ "data-test": "stash-thumbnail-group"
61
+ }, [
62
+ T(e.$slots, "default", y(_({ activeThumbnail: r.value, isDragging: E(i) })), void 0, !0),
63
+ (o(!0), d(x, null, w(f.value, (N, b) => (o(), I(S, {
64
+ key: `empty_thumb_${b}`
65
+ }))), 128))
66
+ ], 512));
67
+ }
68
+ }), $ = /* @__PURE__ */ D(M, [["__scopeId", "data-v-358f73aa"]]);
69
+ export {
70
+ $ as default,
71
+ k as moveSortableElements
72
+ };
73
+ //# sourceMappingURL=ThumbnailGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThumbnailGroup.js","sources":["../src/components/ThumbnailGroup/ThumbnailGroup.vue"],"sourcesContent":["<script lang=\"ts\">\n export * from '../../composables/useSortable/useSortable';\n export * from './ThumbnailGroup.types';\n</script>\n\n<script setup lang=\"ts\" generic=\"Thumbnail extends ThumbnailDefinition\">\n import { computed, provide, ref } from 'vue';\n\n import useSortable, {\n moveSortableElements,\n type SortableOnDragEndEvent,\n type SortableOnDragStartEvent,\n } from '../../composables/useSortable/useSortable';\n import { ThumbnailDefinition } from '../Thumbnail/Thumbnail.types';\n import ThumbnailEmpty from '../ThumbnailEmpty/ThumbnailEmpty.vue';\n import { THUMBNAIL_INJECTION } from './ThumbnailGroup.keys';\n import type { ThumbnailEmptyOptions, ThumbnailRadius } from './ThumbnailGroup.types';\n\n interface ThumbnailProps {\n /**\n * Enable drag and drop functionality\n * @default false\n */\n draggable?: boolean;\n\n /**\n * Thumbnail radius. Can be 'rounded' or 'circle'\n * @default 'rounded'\n */\n radius?: ThumbnailRadius;\n\n /**\n * Shows a close button on the thumbnail when set to true\n * @default false\n */\n removable?: boolean;\n\n /**\n * Shows empty thumbnails when set to true\n */\n showEmpty?: boolean;\n\n /**\n * Empty thumbnail options. Only applicable when `showEmpty` is true\n * @default { max: 6 }\n */\n emptyOptions?: ThumbnailEmptyOptions;\n\n /**\n * List of thumbnails to display\n * @default []\n */\n thumbnails?: Thumbnail[];\n }\n\n const emit = defineEmits<{\n /**\n * Emits when a thumbnail starts dragging\n */\n dragStart: [event: SortableOnDragStartEvent];\n\n /**\n * Emits when a thumbnail stops dragging\n */\n dragEnd: [event: SortableOnDragEndEvent];\n\n /**\n * Emits when the thumbnails are updated\n */\n 'update:thumbnails': [thumbnails: Thumbnail[]];\n }>();\n\n const props = withDefaults(defineProps<ThumbnailProps>(), {\n thumbnails: () => [],\n draggable: false,\n radius: 'rounded',\n variant: 'opaque',\n removable: false,\n showEmpty: false,\n emptyOptions: () => ({ max: 6 }),\n });\n\n /**\n * The active thumbnail index\n */\n const activeThumbnail = defineModel<number>('activeThumbnail', {\n default: -1,\n });\n\n const internalThumbnails = computed({\n get() {\n return props.thumbnails;\n },\n set(value) {\n emit('update:thumbnails', value);\n },\n });\n\n const thumbnailRef = ref<HTMLElement | null>(null);\n const { isDragging } = useSortable({\n ref: thumbnailRef,\n list: internalThumbnails,\n isEnabled: props.draggable,\n ghostClass: 'thumbnail-ghost',\n chosenClass: 'thumbnail-chosen',\n sortInPlace: false,\n onDragStart: (e) => emit('dragStart', e),\n onDragEnd: (e) => {\n activeThumbnail.value = e.newIndex;\n moveSortableElements({ list: internalThumbnails, from: e.oldIndex, to: e.newIndex });\n emit('dragEnd', e);\n },\n });\n\n const emptyThumbnailsCount = computed(() => {\n if (!props.showEmpty) return 0;\n\n const { max } = props.emptyOptions || {};\n\n return internalThumbnails.value.length < max ? max - internalThumbnails.value.length : 0;\n });\n\n provide(\n THUMBNAIL_INJECTION.key,\n Object.freeze({\n draggable: computed(() => props.draggable),\n removable: computed(() => props.removable),\n thumbnails: internalThumbnails,\n radius: props.radius,\n activeThumbnail,\n isDragging,\n }),\n );\n</script>\n\n<template>\n <ul ref=\"thumbnailRef\" class=\"stash-thumbnail-group tw-flex tw-flex-wrap tw-gap-3\" data-test=\"stash-thumbnail-group\">\n <slot v-bind=\"{ activeThumbnail, isDragging }\"></slot>\n\n <ThumbnailEmpty v-for=\"(_, index) in emptyThumbnailsCount\" :key=\"`empty_thumb_${index}`\" />\n </ul>\n</template>\n\n<style>\n .thumbnail-ghost {\n .stash-thumbnail-item__img {\n border: 1px solid var(-color-blue-500);\n mask-image: none !important;\n box-shadow: theme('boxShadow.DEFAULT');\n }\n\n .stash-thumbnail-item__remove {\n opacity: 0;\n }\n }\n</style>\n\n<style scoped>\n :deep(.thumbnail-chosen) {\n opacity: 0 !important;\n }\n</style>\n"],"names":["emit","__emit","props","__props","activeThumbnail","_useModel","internalThumbnails","computed","value","thumbnailRef","ref","isDragging","useSortable","moveSortableElements","emptyThumbnailsCount","max","provide","THUMBNAIL_INJECTION"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuDE,UAAMA,IAAOC,GAiBPC,IAAQC,GAaRC,IAAkBC,EAAoBF,GAAA,iBAE3C,GAEKG,IAAqBC,EAAS;AAAA,MAClC,MAAM;AACJ,eAAOL,EAAM;AAAA,MACf;AAAA,MACA,IAAIM,GAAO;AACT,QAAAR,EAAK,qBAAqBQ,CAAK;AAAA,MACjC;AAAA,IAAA,CACD,GAEKC,IAAeC,EAAwB,IAAI,GAC3C,EAAE,YAAAC,EAAW,IAAIC,EAAY;AAAA,MACjC,KAAKH;AAAA,MACL,MAAMH;AAAA,MACN,WAAWJ,EAAM;AAAA,MACjB,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa,CAAC,MAAMF,EAAK,aAAa,CAAC;AAAA,MACvC,WAAW,CAAC,MAAM;AAChB,QAAAI,EAAgB,QAAQ,EAAE,UACLS,EAAA,EAAE,MAAMP,GAAoB,MAAM,EAAE,UAAU,IAAI,EAAE,SAAA,CAAU,GACnFN,EAAK,WAAW,CAAC;AAAA,MACnB;AAAA,IAAA,CACD,GAEKc,IAAuBP,EAAS,MAAM;AACtC,UAAA,CAACL,EAAM,UAAkB,QAAA;AAE7B,YAAM,EAAE,KAAAa,EAAQ,IAAAb,EAAM,gBAAgB,CAAA;AAEtC,aAAOI,EAAmB,MAAM,SAASS,IAAMA,IAAMT,EAAmB,MAAM,SAAS;AAAA,IAAA,CACxF;AAED,WAAAU;AAAA,MACEC,EAAoB;AAAA,MACpB,OAAO,OAAO;AAAA,QACZ,WAAWV,EAAS,MAAML,EAAM,SAAS;AAAA,QACzC,WAAWK,EAAS,MAAML,EAAM,SAAS;AAAA,QACzC,YAAYI;AAAA,QACZ,QAAQJ,EAAM;AAAA,QACd,iBAAAE;AAAA,QACA,YAAAO;AAAA,MAAA,CACD;AAAA,IAAA;;;;;;;;;;;;;"}
@@ -0,0 +1,16 @@
1
+ import { computed as e, ref as a } from "vue";
2
+ const l = Object.freeze({
3
+ key: Symbol("THUMBNAIL_INJECTION_KEY"),
4
+ defaults: Object.freeze({
5
+ radius: "rounded",
6
+ draggable: e(() => !1),
7
+ removable: e(() => !1),
8
+ thumbnails: e(() => []),
9
+ activeThumbnail: a(void 0),
10
+ isDragging: a(!1)
11
+ })
12
+ });
13
+ export {
14
+ l as T
15
+ };
16
+ //# sourceMappingURL=ThumbnailGroup.keys-D6WL5xQ5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThumbnailGroup.keys-D6WL5xQ5.js","sources":["../src/components/ThumbnailGroup/ThumbnailGroup.keys.ts"],"sourcesContent":["import { computed, ref } from 'vue';\n\nimport { InjectionWithDefaults } from '../../../types/utils';\nimport { ThumbnailDefinition } from '../Thumbnail/Thumbnail.types';\nimport { ThumbnailInjection, ThumbnailRadius } from './ThumbnailGroup.types';\n\n// See: https://vuejs.org/guide/typescript/composition-api.html#typing-provide-inject\nexport const THUMBNAIL_INJECTION: InjectionWithDefaults<ThumbnailInjection<ThumbnailDefinition>> = Object.freeze({\n key: Symbol('THUMBNAIL_INJECTION_KEY'),\n defaults: Object.freeze({\n radius: 'rounded' as ThumbnailRadius,\n draggable: computed(() => false),\n removable: computed(() => false),\n thumbnails: computed(() => [] as ThumbnailDefinition[]),\n activeThumbnail: ref(undefined),\n isDragging: ref(false),\n }),\n});\n"],"names":["THUMBNAIL_INJECTION","computed","ref"],"mappings":";AAOa,MAAAA,IAAsF,OAAO,OAAO;AAAA,EAC/G,KAAK,OAAO,yBAAyB;AAAA,EACrC,UAAU,OAAO,OAAO;AAAA,IACtB,QAAQ;AAAA,IACR,WAAWC,EAAS,MAAM,EAAK;AAAA,IAC/B,WAAWA,EAAS,MAAM,EAAK;AAAA,IAC/B,YAAYA,EAAS,MAAM,EAA2B;AAAA,IACtD,iBAAiBC,EAAI,MAAS;AAAA,IAC9B,YAAYA,EAAI,EAAK;AAAA,EAAA,CACtB;AACH,CAAC;"}
@@ -0,0 +1,191 @@
1
+ import { AllowedComponentProps } from 'vue';
2
+ import { ComponentCustomProps } from 'vue';
3
+ import { ComputedRef } from 'vue';
4
+ import { MaybeRefOrGetter } from 'vue';
5
+ import { Ref } from 'vue';
6
+ import { RendererElement } from 'vue';
7
+ import { RendererNode } from 'vue';
8
+ import { ShallowUnwrapRef } from 'vue';
9
+ import { VNode } from 'vue';
10
+ import { VNodeProps } from 'vue';
11
+
12
+ declare const _default: <Thumbnail extends ThumbnailDefinition>(__VLS_props: {
13
+ radius?: ThumbnailRadius | undefined;
14
+ draggable?: boolean | undefined;
15
+ removable?: boolean | undefined;
16
+ thumbnails?: Thumbnail[] | undefined;
17
+ activeThumbnail?: number | undefined;
18
+ readonly onDragStart?: ((event: SortableOnDragStartEvent) => any) | undefined;
19
+ readonly onDragEnd?: ((event: SortableDragEvent) => any) | undefined;
20
+ readonly "onUpdate:thumbnails"?: ((thumbnails: Thumbnail[]) => any) | undefined;
21
+ showEmpty?: boolean | undefined;
22
+ emptyOptions?: ThumbnailEmptyOptions | undefined;
23
+ } & VNodeProps & AllowedComponentProps & ComponentCustomProps, __VLS_ctx?: {
24
+ attrs: any;
25
+ slots: {
26
+ default?(_: {
27
+ activeThumbnail: number;
28
+ isDragging: boolean;
29
+ }): any;
30
+ };
31
+ emit: ((evt: "dragStart", event: SortableOnDragStartEvent) => void) & ((evt: "dragEnd", event: SortableDragEvent) => void) & ((evt: "update:thumbnails", thumbnails: Thumbnail[]) => void);
32
+ } | undefined, __VLS_expose?: ((exposed: ShallowUnwrapRef< {}>) => void) | undefined, __VLS_setup?: Promise<{
33
+ props: {
34
+ radius?: ThumbnailRadius | undefined;
35
+ draggable?: boolean | undefined;
36
+ removable?: boolean | undefined;
37
+ thumbnails?: Thumbnail[] | undefined;
38
+ activeThumbnail?: number | undefined;
39
+ readonly onDragStart?: ((event: SortableOnDragStartEvent) => any) | undefined;
40
+ readonly onDragEnd?: ((event: SortableDragEvent) => any) | undefined;
41
+ readonly "onUpdate:thumbnails"?: ((thumbnails: Thumbnail[]) => any) | undefined;
42
+ showEmpty?: boolean | undefined;
43
+ emptyOptions?: ThumbnailEmptyOptions | undefined;
44
+ } & VNodeProps & AllowedComponentProps & ComponentCustomProps;
45
+ expose(exposed: ShallowUnwrapRef< {}>): void;
46
+ attrs: any;
47
+ slots: {
48
+ default?(_: {
49
+ activeThumbnail: number;
50
+ isDragging: boolean;
51
+ }): any;
52
+ };
53
+ emit: ((evt: "dragStart", event: SortableOnDragStartEvent) => void) & ((evt: "dragEnd", event: SortableDragEvent) => void) & ((evt: "update:thumbnails", thumbnails: Thumbnail[]) => void);
54
+ }>) => VNode<RendererNode, RendererElement, {
55
+ [key: string]: any;
56
+ }> & {
57
+ __ctx?: {
58
+ props: {
59
+ radius?: ThumbnailRadius | undefined;
60
+ draggable?: boolean | undefined;
61
+ removable?: boolean | undefined;
62
+ thumbnails?: Thumbnail[] | undefined;
63
+ activeThumbnail?: number | undefined;
64
+ readonly onDragStart?: ((event: SortableOnDragStartEvent) => any) | undefined;
65
+ readonly onDragEnd?: ((event: SortableDragEvent) => any) | undefined;
66
+ readonly "onUpdate:thumbnails"?: ((thumbnails: Thumbnail[]) => any) | undefined;
67
+ showEmpty?: boolean | undefined;
68
+ emptyOptions?: ThumbnailEmptyOptions | undefined;
69
+ } & VNodeProps & AllowedComponentProps & ComponentCustomProps;
70
+ expose(exposed: ShallowUnwrapRef< {}>): void;
71
+ attrs: any;
72
+ slots: {
73
+ default?(_: {
74
+ activeThumbnail: number;
75
+ isDragging: boolean;
76
+ }): any;
77
+ };
78
+ emit: ((evt: "dragStart", event: SortableOnDragStartEvent) => void) & ((evt: "dragEnd", event: SortableDragEvent) => void) & ((evt: "update:thumbnails", thumbnails: Thumbnail[]) => void);
79
+ } | undefined;
80
+ };
81
+ export default _default;
82
+
83
+ /**
84
+ * Move sortable elements in the list
85
+ * @param list List of sortable elements
86
+ * @param from Original element position
87
+ * @param to New element position
88
+ */
89
+ export declare function moveSortableElements<ListItem>({ list, from, to }: MoveSortableElementsOptions<ListItem>): void;
90
+
91
+ export declare interface MoveSortableElementsOptions<T> {
92
+ list: RefOrGetter<T[]>;
93
+ from: number;
94
+ to: number;
95
+ }
96
+
97
+ /**
98
+ * Like the MaybeRefOrGetter type from Vue, but without the Maybe. Can be used to ensure the most up-to-date value is available.
99
+ */
100
+ declare type RefOrGetter<T> = Ref<T> | (() => T);
101
+
102
+ export declare interface SortableDragEvent extends DragEvent {
103
+ /**
104
+ * The old index of the dragged element
105
+ */
106
+ oldIndex: number;
107
+ /**
108
+ * The new index of the dragged element
109
+ */
110
+ newIndex: number;
111
+ }
112
+
113
+ export declare type SortableOnDragEndEvent = SortableDragEvent;
114
+
115
+ export declare type SortableOnDragStartEvent = Omit<SortableDragEvent, 'newIndex'>;
116
+
117
+ declare interface ThumbnailDefinition {
118
+ id: number | string;
119
+ imageUrl: string;
120
+ }
121
+
122
+ export declare interface ThumbnailEmptyOptions {
123
+ max: number;
124
+ }
125
+
126
+ export declare interface ThumbnailInjection<Thumbnail> {
127
+ isDragging: MaybeRefOrGetter<boolean>;
128
+ radius: ThumbnailRadius;
129
+ draggable: ComputedRef<boolean>;
130
+ removable: MaybeRefOrGetter<boolean>;
131
+ thumbnails: MaybeRefOrGetter<Thumbnail[]>;
132
+ activeThumbnail: Ref<number | undefined>;
133
+ }
134
+
135
+ export declare type ThumbnailRadius = 'rounded' | 'circle';
136
+
137
+ export declare interface UseSortableOptions<SortableItem> {
138
+ /**
139
+ * The parent element of the sortable elements
140
+ */
141
+ ref: Ref<HTMLElement | null>;
142
+ /**
143
+ * The list of sortable elements to be sorted
144
+ */
145
+ list: Ref<SortableItem[]>;
146
+ /**
147
+ * Enables/Disables drag and drop sorting
148
+ * @default true
149
+ */
150
+ isEnabled?: boolean;
151
+ /**
152
+ * Class name for the ghost element
153
+ * @default ''
154
+ */
155
+ ghostClass?: string;
156
+ /**
157
+ * Class name for the chosen element
158
+ * @default ''
159
+ */
160
+ chosenClass?: string;
161
+ /**
162
+ * Sort the list in place
163
+ * @default true
164
+ */
165
+ sortInPlace?: boolean;
166
+ /**
167
+ * Callback when the dragging starts
168
+ */
169
+ onDragStart?: (e: SortableOnDragStartEvent) => void;
170
+ /**
171
+ * Callback when the dragging ends
172
+ */
173
+ onDragEnd?: (e: SortableOnDragEndEvent) => void;
174
+ }
175
+
176
+ export declare interface UseSortableReturn {
177
+ /**
178
+ * Whether the element is currently being dragged
179
+ */
180
+ isDragging: Ref<boolean>;
181
+ /**
182
+ * The new position of the dragged element
183
+ */
184
+ newIndex: Ref<number>;
185
+ /**
186
+ * The original position of the dragged element
187
+ */
188
+ oldIndex: Ref<number>;
189
+ }
190
+
191
+ export { }
package/dist/Tooltip.js CHANGED
@@ -1,6 +1,6 @@
1
- import { defineComponent as g, ref as n, computed as r, openBlock as d, createElementBlock as S, createElementVNode as i, renderSlot as l, createBlock as T, Teleport as E, normalizeStyle as u, unref as $, createTextVNode as k, toDisplayString as O } from "vue";
1
+ import { defineComponent as g, ref as n, computed as r, openBlock as d, createElementBlock as S, createElementVNode as i, renderSlot as l, createBlock as T, Teleport as E, normalizeStyle as f, unref as $, createTextVNode as k, toDisplayString as O } from "vue";
2
2
  import { u as A, a as B, f as F, o as D, b as I } from "./floating-ui.vue-BmfQDqE-.js";
3
- import { u as P } from "./index-Ck3Dl09q.js";
3
+ import { a as P } from "./index-Bbc2pg2X.js";
4
4
  const z = {
5
5
  ref: "wrapper",
6
6
  class: "stash-tooltip__wrapper tw-relative tw-inline-flex tw-h-fit tw-w-fit"
@@ -13,13 +13,13 @@ const z = {
13
13
  teleportTo: { default: "#stash-menus-mount-node" },
14
14
  text: { default: "" }
15
15
  },
16
- setup(f) {
16
+ setup(u) {
17
17
  const m = {
18
18
  top: "bottom",
19
19
  right: "left",
20
20
  bottom: "top",
21
21
  left: "right"
22
- }, e = f, a = n(null), p = n(null), c = n(null), { isOutside: h } = P(a), _ = r(() => !h.value || e.isOpen), y = r(() => e.side), { floatingStyles: x, middlewareData: o, placement: v } = A(a, c, {
22
+ }, e = u, a = n(null), p = n(null), c = n(null), { isOutside: h } = P(a), _ = r(() => !h.value || e.isOpen), y = r(() => e.side), { floatingStyles: x, middlewareData: o, placement: v } = A(a, c, {
23
23
  whileElementsMounted: B,
24
24
  placement: y,
25
25
  middleware: [F(), D(N), I({ element: p })]
@@ -51,7 +51,7 @@ const z = {
51
51
  "data-test": "stash-tooltip",
52
52
  class: "stash-tooltip tw-pointer-events-none tw-z-screen tw-flex tw-w-[148px] tw-flex-col tw-items-center tw-whitespace-normal tw-rounded tw-bg-ice-900 tw-p-3 tw-text-center tw-text-xs tw-text-white tw-opacity-0 tw-shadow tw-transition-opacity",
53
53
  role: "tooltip",
54
- style: u({
54
+ style: f({
55
55
  ...$(x),
56
56
  opacity: _.value ? 0.95 : 0
57
57
  })
@@ -65,7 +65,7 @@ const z = {
65
65
  ref_key: "floatingArrow",
66
66
  ref: p,
67
67
  class: "stash-tooltip__arrow tw-absolute tw-z-behind tw-h-[12px] tw-w-[12px] tw-rotate-45 tw-bg-ice-900",
68
- style: u(b.value)
68
+ style: f(b.value)
69
69
  }, null, 4)
70
70
  ], 4)
71
71
  ], 8, ["to", "disabled"]))