nuxt-ui-tools 1.23.0 → 1.25.0

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.
Files changed (226) hide show
  1. package/dist/module.d.mts +3 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +8 -0
  4. package/dist/runtime/display/components/blocks/display-accordion.d.vue.ts +4 -0
  5. package/dist/runtime/display/components/blocks/display-accordion.vue +93 -0
  6. package/dist/runtime/display/components/blocks/display-accordion.vue.d.ts +4 -0
  7. package/dist/runtime/display/components/blocks/display-alert.d.vue.ts +4 -0
  8. package/dist/runtime/display/components/blocks/display-alert.vue +133 -0
  9. package/dist/runtime/display/components/blocks/display-alert.vue.d.ts +4 -0
  10. package/dist/runtime/display/components/blocks/display-breakdown.d.vue.ts +4 -0
  11. package/dist/runtime/display/components/blocks/display-breakdown.vue +178 -0
  12. package/dist/runtime/display/components/blocks/display-breakdown.vue.d.ts +4 -0
  13. package/dist/runtime/display/components/blocks/display-chart.d.vue.ts +4 -0
  14. package/dist/runtime/display/components/blocks/display-chart.vue +237 -0
  15. package/dist/runtime/display/components/blocks/display-chart.vue.d.ts +4 -0
  16. package/dist/runtime/display/components/blocks/display-code.d.vue.ts +4 -0
  17. package/dist/runtime/display/components/blocks/display-code.vue +143 -0
  18. package/dist/runtime/display/components/blocks/display-code.vue.d.ts +4 -0
  19. package/dist/runtime/display/components/blocks/display-date.d.vue.ts +4 -0
  20. package/dist/runtime/display/components/blocks/display-date.vue +174 -0
  21. package/dist/runtime/display/components/blocks/display-date.vue.d.ts +4 -0
  22. package/dist/runtime/display/components/blocks/display-diff.d.vue.ts +4 -0
  23. package/dist/runtime/display/components/blocks/display-diff.vue +142 -0
  24. package/dist/runtime/display/components/blocks/display-diff.vue.d.ts +4 -0
  25. package/dist/runtime/display/components/blocks/display-empty.d.vue.ts +4 -0
  26. package/dist/runtime/display/components/blocks/display-empty.vue +74 -0
  27. package/dist/runtime/display/components/blocks/display-empty.vue.d.ts +4 -0
  28. package/dist/runtime/display/components/blocks/display-facts.d.vue.ts +4 -0
  29. package/dist/runtime/display/components/blocks/display-facts.vue +180 -0
  30. package/dist/runtime/display/components/blocks/display-facts.vue.d.ts +4 -0
  31. package/dist/runtime/display/components/blocks/display-heading.d.vue.ts +4 -0
  32. package/dist/runtime/display/components/blocks/display-heading.vue +96 -0
  33. package/dist/runtime/display/components/blocks/display-heading.vue.d.ts +4 -0
  34. package/dist/runtime/display/components/blocks/display-list.d.vue.ts +4 -0
  35. package/dist/runtime/display/components/blocks/display-list.vue +295 -0
  36. package/dist/runtime/display/components/blocks/display-list.vue.d.ts +4 -0
  37. package/dist/runtime/display/components/blocks/display-media.d.vue.ts +4 -0
  38. package/dist/runtime/display/components/blocks/display-media.vue +326 -0
  39. package/dist/runtime/display/components/blocks/display-media.vue.d.ts +4 -0
  40. package/dist/runtime/display/components/blocks/display-meter.d.vue.ts +4 -0
  41. package/dist/runtime/display/components/blocks/display-meter.vue +301 -0
  42. package/dist/runtime/display/components/blocks/display-meter.vue.d.ts +4 -0
  43. package/dist/runtime/display/components/blocks/display-person.d.vue.ts +4 -0
  44. package/dist/runtime/display/components/blocks/display-person.vue +146 -0
  45. package/dist/runtime/display/components/blocks/display-person.vue.d.ts +4 -0
  46. package/dist/runtime/display/components/blocks/display-record.d.vue.ts +4 -0
  47. package/dist/runtime/display/components/blocks/display-record.vue +208 -0
  48. package/dist/runtime/display/components/blocks/display-record.vue.d.ts +4 -0
  49. package/dist/runtime/display/components/blocks/display-stat.d.vue.ts +4 -0
  50. package/dist/runtime/display/components/blocks/display-stat.vue +178 -0
  51. package/dist/runtime/display/components/blocks/display-stat.vue.d.ts +4 -0
  52. package/dist/runtime/display/components/blocks/display-table.d.vue.ts +4 -0
  53. package/dist/runtime/display/components/blocks/display-table.vue +132 -0
  54. package/dist/runtime/display/components/blocks/display-table.vue.d.ts +4 -0
  55. package/dist/runtime/display/components/blocks/display-text.d.vue.ts +4 -0
  56. package/dist/runtime/display/components/blocks/display-text.vue +135 -0
  57. package/dist/runtime/display/components/blocks/display-text.vue.d.ts +4 -0
  58. package/dist/runtime/display/components/blocks/display-timeline.d.vue.ts +4 -0
  59. package/dist/runtime/display/components/blocks/display-timeline.vue +232 -0
  60. package/dist/runtime/display/components/blocks/display-timeline.vue.d.ts +4 -0
  61. package/dist/runtime/display/components/display.d.vue.ts +24 -0
  62. package/dist/runtime/display/components/display.vue +21 -0
  63. package/dist/runtime/display/components/display.vue.d.ts +24 -0
  64. package/dist/runtime/display/components/parts/display-actions.d.vue.ts +9 -0
  65. package/dist/runtime/display/components/parts/display-actions.vue +24 -0
  66. package/dist/runtime/display/components/parts/display-actions.vue.d.ts +9 -0
  67. package/dist/runtime/display/components/parts/display-chip.d.vue.ts +11 -0
  68. package/dist/runtime/display/components/parts/display-chip.vue +45 -0
  69. package/dist/runtime/display/components/parts/display-chip.vue.d.ts +11 -0
  70. package/dist/runtime/display/components/parts/display-copy.d.vue.ts +6 -0
  71. package/dist/runtime/display/components/parts/display-copy.vue +27 -0
  72. package/dist/runtime/display/components/parts/display-copy.vue.d.ts +6 -0
  73. package/dist/runtime/display/components/parts/display-empty-line.d.vue.ts +7 -0
  74. package/dist/runtime/display/components/parts/display-empty-line.vue +21 -0
  75. package/dist/runtime/display/components/parts/display-empty-line.vue.d.ts +7 -0
  76. package/dist/runtime/display/components/parts/display-fact-value.d.vue.ts +7 -0
  77. package/dist/runtime/display/components/parts/display-fact-value.vue +35 -0
  78. package/dist/runtime/display/components/parts/display-fact-value.vue.d.ts +7 -0
  79. package/dist/runtime/display/components/parts/display-load-error.d.vue.ts +7 -0
  80. package/dist/runtime/display/components/parts/display-load-error.vue +29 -0
  81. package/dist/runtime/display/components/parts/display-load-error.vue.d.ts +7 -0
  82. package/dist/runtime/display/components/parts/display-more.d.vue.ts +10 -0
  83. package/dist/runtime/display/components/parts/display-more.vue +23 -0
  84. package/dist/runtime/display/components/parts/display-more.vue.d.ts +10 -0
  85. package/dist/runtime/display/components/parts/display-record-media.d.vue.ts +9 -0
  86. package/dist/runtime/display/components/parts/display-record-media.vue +59 -0
  87. package/dist/runtime/display/components/parts/display-record-media.vue.d.ts +9 -0
  88. package/dist/runtime/display/components/parts/display-render.d.ts +10 -0
  89. package/dist/runtime/display/components/parts/display-render.js +5 -0
  90. package/dist/runtime/display/components/parts/display-skeleton.d.vue.ts +7 -0
  91. package/dist/runtime/display/components/parts/display-skeleton.vue +67 -0
  92. package/dist/runtime/display/components/parts/display-skeleton.vue.d.ts +7 -0
  93. package/dist/runtime/display/composables/use-display-config.d.ts +6 -0
  94. package/dist/runtime/display/composables/use-display-config.js +11 -0
  95. package/dist/runtime/display/composables/use-display-file-preview.d.ts +9 -0
  96. package/dist/runtime/display/composables/use-display-file-preview.js +11 -0
  97. package/dist/runtime/display/composables/use-display-icons.d.ts +8 -0
  98. package/dist/runtime/display/composables/use-display-icons.js +62 -0
  99. package/dist/runtime/display/composables/use-display-locale.d.ts +5 -0
  100. package/dist/runtime/display/composables/use-display-locale.js +5 -0
  101. package/dist/runtime/display/composables/use-display-number.d.ts +2 -0
  102. package/dist/runtime/display/composables/use-display-number.js +7 -0
  103. package/dist/runtime/display/composables/use-display-reveal.d.ts +14 -0
  104. package/dist/runtime/display/composables/use-display-reveal.js +15 -0
  105. package/dist/runtime/display/composables/use-display-url.d.ts +26 -0
  106. package/dist/runtime/display/composables/use-display-url.js +46 -0
  107. package/dist/runtime/display/index.d.ts +4 -0
  108. package/dist/runtime/display/index.js +3 -0
  109. package/dist/runtime/display/plugins/display-tones.d.ts +3 -0
  110. package/dist/runtime/display/plugins/display-tones.js +17 -0
  111. package/dist/runtime/display/types/blocks/accordion.d.ts +18 -0
  112. package/dist/runtime/display/types/blocks/accordion.js +0 -0
  113. package/dist/runtime/display/types/blocks/alert.d.ts +31 -0
  114. package/dist/runtime/display/types/blocks/alert.js +0 -0
  115. package/dist/runtime/display/types/blocks/breakdown.d.ts +36 -0
  116. package/dist/runtime/display/types/blocks/breakdown.js +0 -0
  117. package/dist/runtime/display/types/blocks/chart.d.ts +23 -0
  118. package/dist/runtime/display/types/blocks/chart.js +0 -0
  119. package/dist/runtime/display/types/blocks/code.d.ts +21 -0
  120. package/dist/runtime/display/types/blocks/code.js +0 -0
  121. package/dist/runtime/display/types/blocks/date.d.ts +27 -0
  122. package/dist/runtime/display/types/blocks/date.js +0 -0
  123. package/dist/runtime/display/types/blocks/diff.d.ts +20 -0
  124. package/dist/runtime/display/types/blocks/diff.js +0 -0
  125. package/dist/runtime/display/types/blocks/empty.d.ts +13 -0
  126. package/dist/runtime/display/types/blocks/empty.js +0 -0
  127. package/dist/runtime/display/types/blocks/facts.d.ts +50 -0
  128. package/dist/runtime/display/types/blocks/facts.js +0 -0
  129. package/dist/runtime/display/types/blocks/heading.d.ts +19 -0
  130. package/dist/runtime/display/types/blocks/heading.js +0 -0
  131. package/dist/runtime/display/types/blocks/list.d.ts +36 -0
  132. package/dist/runtime/display/types/blocks/list.js +0 -0
  133. package/dist/runtime/display/types/blocks/media.d.ts +42 -0
  134. package/dist/runtime/display/types/blocks/media.js +0 -0
  135. package/dist/runtime/display/types/blocks/meter.d.ts +48 -0
  136. package/dist/runtime/display/types/blocks/meter.js +0 -0
  137. package/dist/runtime/display/types/blocks/person.d.ts +32 -0
  138. package/dist/runtime/display/types/blocks/person.js +0 -0
  139. package/dist/runtime/display/types/blocks/record.d.ts +42 -0
  140. package/dist/runtime/display/types/blocks/record.js +0 -0
  141. package/dist/runtime/display/types/blocks/stat.d.ts +44 -0
  142. package/dist/runtime/display/types/blocks/stat.js +0 -0
  143. package/dist/runtime/display/types/blocks/table.d.ts +28 -0
  144. package/dist/runtime/display/types/blocks/table.js +0 -0
  145. package/dist/runtime/display/types/blocks/text.d.ts +22 -0
  146. package/dist/runtime/display/types/blocks/text.js +0 -0
  147. package/dist/runtime/display/types/blocks/timeline.d.ts +37 -0
  148. package/dist/runtime/display/types/blocks/timeline.js +0 -0
  149. package/dist/runtime/display/types/config.d.ts +53 -0
  150. package/dist/runtime/display/types/config.js +0 -0
  151. package/dist/runtime/display/types/index.d.ts +22 -0
  152. package/dist/runtime/display/types/index.js +0 -0
  153. package/dist/runtime/display/types/registry.d.ts +57 -0
  154. package/dist/runtime/display/types/registry.js +0 -0
  155. package/dist/runtime/display/types/shared.d.ts +71 -0
  156. package/dist/runtime/display/types/shared.js +0 -0
  157. package/dist/runtime/display/utils/classes.d.ts +3 -0
  158. package/dist/runtime/display/utils/classes.js +7 -0
  159. package/dist/runtime/display/utils/config.d.ts +6 -0
  160. package/dist/runtime/display/utils/config.js +7 -0
  161. package/dist/runtime/display/utils/content.d.ts +7 -0
  162. package/dist/runtime/display/utils/content.js +13 -0
  163. package/dist/runtime/display/utils/dates.d.ts +16 -0
  164. package/dist/runtime/display/utils/dates.js +34 -0
  165. package/dist/runtime/display/utils/json.d.ts +7 -0
  166. package/dist/runtime/display/utils/json.js +15 -0
  167. package/dist/runtime/display/utils/meter.d.ts +7 -0
  168. package/dist/runtime/display/utils/meter.js +13 -0
  169. package/dist/runtime/display/utils/registry.d.ts +47 -0
  170. package/dist/runtime/display/utils/registry.js +64 -0
  171. package/dist/runtime/display/utils/tone-css.d.ts +7 -0
  172. package/dist/runtime/display/utils/tone-css.js +38 -0
  173. package/dist/runtime/display/utils/tone.d.ts +26 -0
  174. package/dist/runtime/display/utils/tone.js +38 -0
  175. package/dist/runtime/display/utils/urls.d.ts +10 -0
  176. package/dist/runtime/display/utils/urls.js +33 -0
  177. package/dist/runtime/file-preview/components/shell/file-preview-stage.vue +5 -3
  178. package/dist/runtime/file-preview/composables/use-file-preview-source.d.ts +7 -0
  179. package/dist/runtime/file-preview/composables/use-file-preview-source.js +7 -1
  180. package/dist/runtime/file-preview/index.d.ts +1 -0
  181. package/dist/runtime/file-preview/index.js +1 -0
  182. package/dist/runtime/file-preview/types/file.d.ts +9 -0
  183. package/dist/runtime/file-preview/utils/errors.d.ts +30 -0
  184. package/dist/runtime/file-preview/utils/errors.js +26 -0
  185. package/dist/runtime/form/components/utils/form-file-preview.vue +7 -2
  186. package/dist/runtime/form/composables/use-form-root.js +2 -0
  187. package/dist/runtime/form/composables/use-form-runtime.js +7 -2
  188. package/dist/runtime/form/fields/index.d.ts +5 -0
  189. package/dist/runtime/form/fields/info/component.vue +51 -19
  190. package/dist/runtime/form/fields/info/config.d.ts +5 -0
  191. package/dist/runtime/form/fields/info/config.js +2 -1
  192. package/dist/runtime/form/fields/info/types.d.ts +78 -4
  193. package/dist/runtime/form/fields/info/use-info-display.d.ts +20 -0
  194. package/dist/runtime/form/fields/info/use-info-display.js +82 -0
  195. package/dist/runtime/form/fields/upload/upload-item.vue +10 -2
  196. package/dist/runtime/form/types/api.d.ts +6 -0
  197. package/dist/runtime/form/types/runtime.d.ts +2 -0
  198. package/dist/runtime/form/utils/dynamic.d.ts +6 -0
  199. package/dist/runtime/form/utils/dynamic.js +7 -0
  200. package/dist/runtime/i18n/locales/en.js +32 -0
  201. package/dist/runtime/i18n/locales/fr.js +32 -0
  202. package/dist/runtime/i18n/types.d.ts +40 -0
  203. package/dist/runtime/shared/styles/tokens.css +1 -1
  204. package/dist/runtime/shared/utils/file.d.ts +1 -1
  205. package/dist/runtime/table/components/data-list/data-list-root.vue +2 -0
  206. package/dist/runtime/table/components/data-list/data-list-selection-actions.d.vue.ts +1 -1
  207. package/dist/runtime/table/components/data-list/data-list-selection-actions.vue +32 -5
  208. package/dist/runtime/table/components/data-list/data-list-selection-actions.vue.d.ts +1 -1
  209. package/dist/runtime/table/components/data-list/data-list-selection-sheet.d.vue.ts +10 -0
  210. package/dist/runtime/table/components/data-list/data-list-selection-sheet.vue +238 -0
  211. package/dist/runtime/table/components/data-list/data-list-selection-sheet.vue.d.ts +10 -0
  212. package/dist/runtime/table/components/grid/grid-renderer.vue +2 -0
  213. package/dist/runtime/table/components/summary/summary-renderer.vue +2 -0
  214. package/dist/runtime/table/components/table/table-renderer.vue +2 -0
  215. package/dist/runtime/table/components/utils/overlay-inset-spacer.d.vue.ts +3 -0
  216. package/dist/runtime/table/components/utils/overlay-inset-spacer.vue +13 -0
  217. package/dist/runtime/table/components/utils/overlay-inset-spacer.vue.d.ts +3 -0
  218. package/dist/runtime/table/composables/use-data-list-overlay-inset.d.ts +10 -0
  219. package/dist/runtime/table/composables/use-data-list-overlay-inset.js +11 -0
  220. package/dist/runtime/table/composables/use-table-actions.js +11 -2
  221. package/dist/runtime/table/constants/mobile.d.ts +0 -7
  222. package/dist/runtime/table/constants/mobile.js +0 -1
  223. package/dist/runtime/table/types/actions.d.ts +7 -0
  224. package/dist/runtime/table/types/data-list.d.ts +8 -1
  225. package/dist/runtime/table/utils/data-list-ui.d.ts +1 -1
  226. package/package.json +16 -1
@@ -0,0 +1,295 @@
1
+ <script setup>
2
+ import UButton from "@nuxt/ui/components/Button.vue";
3
+ import UIcon from "@nuxt/ui/components/Icon.vue";
4
+ import { computed } from "vue";
5
+ import { fileExtensionLabel, formatFileSize } from "../../../shared/utils/file";
6
+ import { isString } from "../../../shared/utils/predicate";
7
+ import { useDisplayAppUi } from "../../composables/use-display-config";
8
+ import { useDisplayFilePreview } from "../../composables/use-display-file-preview";
9
+ import { useDisplayIcons } from "../../composables/use-display-icons";
10
+ import { useDisplayLocale } from "../../composables/use-display-locale";
11
+ import { useDisplayReveal } from "../../composables/use-display-reveal";
12
+ import { useDisplayUrlActions } from "../../composables/use-display-url";
13
+ import { resolveDisplayClasses } from "../../utils/classes";
14
+ import { displayContentText, isDisplayContentEmpty, resolveDisplayText } from "../../utils/content";
15
+ import { displayToneClass } from "../../utils/tone";
16
+ import { downloadDisplayUrl, openDisplayUrl } from "../../utils/urls";
17
+ import DisplayChip from "../parts/display-chip.vue";
18
+ import DisplayEmptyLine from "../parts/display-empty-line.vue";
19
+ import DisplayLoadError from "../parts/display-load-error.vue";
20
+ import DisplayMore from "../parts/display-more.vue";
21
+ import DisplayRender from "../parts/display-render";
22
+ import DisplaySkeleton from "../parts/display-skeleton.vue";
23
+ const {
24
+ variant = "bullets",
25
+ title = void 0,
26
+ items,
27
+ columns = 2,
28
+ preview = true,
29
+ max = void 0,
30
+ step = void 0,
31
+ empty = void 0,
32
+ loading = false,
33
+ ui = void 0
34
+ } = defineProps({
35
+ variant: { type: String, required: false },
36
+ title: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
37
+ items: { type: Array, required: true },
38
+ columns: { type: Number, required: false },
39
+ preview: { type: Boolean, required: false },
40
+ loading: { type: Boolean, required: false },
41
+ ui: { type: Object, required: false },
42
+ max: { type: Number, required: false },
43
+ step: { type: Number, required: false },
44
+ empty: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true }
45
+ });
46
+ const LIST_CLASSES = {
47
+ bullets: "flex flex-col gap-1.5",
48
+ checks: "flex flex-col gap-1.5",
49
+ chips: "flex flex-wrap gap-1.5",
50
+ columns: "block",
51
+ files: "flex flex-col divide-y divide-muted rounded-[var(--nut-display-radius)] border border-default",
52
+ icons: "flex flex-col gap-1.5",
53
+ numbered: "flex flex-col gap-1.5",
54
+ steps: "flex flex-col"
55
+ };
56
+ const ITEM_CLASSES = {
57
+ bullets: "relative ps-4",
58
+ checks: "flex items-start gap-2",
59
+ chips: "inline-flex",
60
+ columns: "relative mb-1.5 break-inside-avoid ps-4",
61
+ files: "flex min-w-0 items-center gap-2.5 px-2.5 py-2",
62
+ icons: "flex items-start gap-2",
63
+ numbered: "flex items-start gap-2.5",
64
+ steps: "relative grid grid-cols-[1.375rem_minmax(0,1fr)] gap-x-2.5 pb-3 before:absolute before:start-[0.6875rem] before:top-6 before:bottom-0 before:w-px before:bg-[var(--ui-border)] last:pb-0 last:before:hidden"
65
+ };
66
+ const MARKER_CLASSES = {
67
+ bullets: "absolute start-1 top-[0.6em] size-1.5 rounded-full bg-[var(--ui-border-accented)]",
68
+ checks: "mt-0.5 size-4 shrink-0",
69
+ chips: "",
70
+ columns: "absolute start-1 top-[0.6em] size-1.5 rounded-full bg-[var(--ui-border-accented)]",
71
+ files: "grid h-8 w-7 shrink-0 place-items-center rounded-[4px] bg-[var(--nut-tone)] text-[8.5px] font-extrabold tracking-[0.02em] text-[var(--nut-tone-contrast)]",
72
+ icons: "mt-0.5 size-4 shrink-0 text-muted",
73
+ numbered: "grid size-5 shrink-0 place-items-center rounded-full bg-elevated text-[11px] font-bold text-toned",
74
+ steps: "grid size-5.5 place-items-center rounded-full text-[11px] font-bold"
75
+ };
76
+ const FILE_TONES = [
77
+ [["pdf"], "error"],
78
+ [["xls", "xlsx", "csv", "ods"], "success"],
79
+ [["doc", "docx", "odt", "txt", "md"], "info"],
80
+ [["png", "jpg", "jpeg", "gif", "webp", "svg", "avif"], "secondary"],
81
+ [["zip", "rar", "7z", "gz"], "warning"]
82
+ ];
83
+ const { t, code } = useDisplayLocale();
84
+ const icons = useDisplayIcons();
85
+ const filePreview = useDisplayFilePreview();
86
+ const links = useDisplayUrlActions();
87
+ const appUi = useDisplayAppUi("list");
88
+ const reveal = useDisplayReveal({ items: () => items, max: () => max, step: () => step });
89
+ const files = computed(
90
+ () => items.map((item) => ({
91
+ item,
92
+ label: fileExtensionLabel({ name: fileName(item), type: item.type }),
93
+ size: item.size === void 0 ? void 0 : formatFileSize(item.size, code.value)
94
+ }))
95
+ );
96
+ const classes = computed(
97
+ () => resolveDisplayClasses(
98
+ {
99
+ action: "ms-auto shrink-0",
100
+ description: variant === "files" ? "text-[11px] text-dimmed" : "text-xs text-muted",
101
+ empty: "",
102
+ item: ITEM_CLASSES[variant],
103
+ list: `m-0 list-none p-0 text-sm ${LIST_CLASSES[variant]}`,
104
+ marker: MARKER_CLASSES[variant],
105
+ meta: "ms-auto shrink-0 text-xs text-dimmed",
106
+ more: "self-start",
107
+ root: "flex min-w-0 flex-col gap-2",
108
+ text: variant === "files" ? "truncate font-medium text-highlighted" : "min-w-0 text-default",
109
+ title: "text-sm font-semibold text-toned"
110
+ },
111
+ appUi.value,
112
+ ui
113
+ )
114
+ );
115
+ const ordered = computed(() => variant === "numbered" || variant === "steps");
116
+ function fileName(item) {
117
+ return item.name ?? displayContentText(item.text) ?? (isString(item.href) ? item.href : "");
118
+ }
119
+ function fileTone(item) {
120
+ const extension = fileName(item).split(".").at(-1)?.toLowerCase() ?? "";
121
+ return FILE_TONES.find(([extensions]) => extensions.includes(extension))?.[1] ?? "neutral";
122
+ }
123
+ function stepState(item) {
124
+ if (item.done) return "done";
125
+ return item.current ? "current" : "todo";
126
+ }
127
+ function stepClass(item) {
128
+ const state = stepState(item);
129
+ if (state === "done")
130
+ return "bg-[var(--nut-display-success-soft)] text-[var(--nut-display-success-ink)]";
131
+ if (state === "current")
132
+ return "bg-[var(--nut-tone)] text-[var(--nut-tone-contrast)] ring-3 ring-[var(--nut-tone-soft)]";
133
+ return "bg-elevated text-dimmed";
134
+ }
135
+ function openFile(event, index) {
136
+ const href = items[index]?.href;
137
+ if (!href) return;
138
+ const previewed = preview && filePreview.open(
139
+ items.flatMap(
140
+ (item) => item.href ? [{ mime: item.type, name: fileName(item), size: item.size, src: item.href }] : []
141
+ ),
142
+ items.slice(0, index).filter((item) => item.href).length
143
+ );
144
+ if (previewed) {
145
+ event.preventDefault();
146
+ return;
147
+ }
148
+ if (isString(href)) return;
149
+ event.preventDefault();
150
+ void links.run(`open-${index}`, () => openDisplayUrl(href));
151
+ }
152
+ function downloadFile(index) {
153
+ const item = items[index];
154
+ if (!item?.href) return;
155
+ const href = item.href;
156
+ void links.run(`download-${index}`, () => downloadDisplayUrl(href, fileName(item)));
157
+ }
158
+ </script>
159
+
160
+ <template>
161
+ <DisplaySkeleton v-if="loading" :count="Math.min(Math.max(items.length, 2), 5)" />
162
+ <div v-else :class="classes.root" data-display="list" :data-variant="variant">
163
+ <div v-if="!isDisplayContentEmpty(title)" :class="classes.title">
164
+ <DisplayRender :content="title" />
165
+ </div>
166
+ <DisplayEmptyLine v-if="!items.length" :content="empty" :class="classes.empty" />
167
+ <component
168
+ :is="ordered ? 'ol' : 'ul'"
169
+ v-else
170
+ :class="classes.list"
171
+ :style="variant === 'columns' ? { columnCount: columns, columnGap: '1.25rem' } : void 0"
172
+ >
173
+ <li
174
+ v-for="(item, index) in reveal.visible.value"
175
+ :key="index"
176
+ :class="[
177
+ classes.item,
178
+ item.tone ? displayToneClass(item.tone) : displayToneClass('primary')
179
+ ]"
180
+ :data-state="
181
+ variant === 'steps' ? stepState(item) : variant === 'checks' ? item.done ? 'done' : 'todo' : void 0
182
+ "
183
+ >
184
+ <template v-if="variant === 'chips'">
185
+ <DisplayChip
186
+ :chip="{ icon: item.icon, size: 'md', tone: item.tone ?? 'neutral' }"
187
+ :content="item.text"
188
+ :title="resolveDisplayText(item.tooltip)"
189
+ />
190
+ </template>
191
+ <template v-else-if="variant === 'files'">
192
+ <span :class="[classes.marker, displayToneClass(fileTone(item))]" aria-hidden="true">
193
+ {{ files[index]?.label }}
194
+ </span>
195
+ <div class="min-w-0 flex-1">
196
+ <div :class="classes.text">
197
+ <a
198
+ v-if="item.href"
199
+ :href="isString(item.href) ? item.href : '#'"
200
+ target="_blank"
201
+ rel="noopener"
202
+ class="hover:underline"
203
+ :aria-busy="links.pending.value === `open-${index}` || void 0"
204
+ @click="openFile($event, index)"
205
+ >
206
+ <DisplayRender :content="item.text" />
207
+ </a>
208
+ <DisplayRender v-else :content="item.text" />
209
+ </div>
210
+ <div
211
+ v-if="files[index]?.size || !isDisplayContentEmpty(item.description)"
212
+ :class="classes.description"
213
+ >
214
+ {{ files[index]?.size
215
+ }}<template v-if="files[index]?.size && !isDisplayContentEmpty(item.description)">
216
+ ·
217
+ </template>
218
+ <DisplayRender :content="item.description" />
219
+ </div>
220
+ </div>
221
+ <UButton
222
+ v-if="item.href"
223
+ :aria-label="t('display.download')"
224
+ :loading="
225
+ links.pending.value === `download-${index}` || links.pending.value === `open-${index}`
226
+ "
227
+ icon="i-lucide-download"
228
+ color="neutral"
229
+ variant="ghost"
230
+ size="xs"
231
+ square
232
+ :class="classes.action"
233
+ data-display-download
234
+ @click="downloadFile(index)"
235
+ />
236
+ </template>
237
+ <template v-else>
238
+ <span
239
+ v-if="variant === 'bullets' || variant === 'columns'"
240
+ :class="classes.marker"
241
+ aria-hidden="true"
242
+ />
243
+ <span v-else-if="variant === 'numbered'" :class="classes.marker" aria-hidden="true">{{
244
+ index + 1
245
+ }}</span>
246
+ <UIcon
247
+ v-else-if="variant === 'icons'"
248
+ :name="item.icon ?? icons.chevronRight"
249
+ :class="[classes.marker, item.tone ? 'text-[var(--nut-tone-ink)]' : '']"
250
+ />
251
+ <UIcon
252
+ v-else-if="variant === 'checks'"
253
+ :name="item.done ? icons.success : 'i-lucide-circle'"
254
+ :class="[
255
+ classes.marker,
256
+ item.done ? 'text-[var(--nut-display-success-ink)]' : 'text-dimmed'
257
+ ]"
258
+ />
259
+ <span
260
+ v-else-if="variant === 'steps'"
261
+ :class="[classes.marker, stepClass(item)]"
262
+ aria-hidden="true"
263
+ >
264
+ <UIcon v-if="item.done" :name="icons.check" class="size-3" />
265
+ <template v-else>{{ index + 1 }}</template>
266
+ </span>
267
+ <div class="min-w-0 flex-1">
268
+ <div
269
+ :class="[
270
+ classes.text,
271
+ variant === 'steps' ? stepState(item) === 'todo' ? 'leading-[1.375rem] font-medium text-muted' : 'leading-[1.375rem] font-semibold text-highlighted' : '',
272
+ variant === 'checks' && !item.done ? 'text-muted' : ''
273
+ ]"
274
+ >
275
+ <DisplayRender :content="item.text" />
276
+ </div>
277
+ <div v-if="!isDisplayContentEmpty(item.description)" :class="classes.description">
278
+ <DisplayRender :content="item.description" />
279
+ </div>
280
+ </div>
281
+ <span v-if="!isDisplayContentEmpty(item.meta)" :class="classes.meta"
282
+ ><DisplayRender :content="item.meta"
283
+ /></span>
284
+ </template>
285
+ </li>
286
+ </component>
287
+ <DisplayLoadError v-if="links.failed.value" @retry="links.failed.value?.retry()" />
288
+ <DisplayMore
289
+ v-if="reveal.hidden.value"
290
+ :count="reveal.next.value"
291
+ :class="classes.more"
292
+ @reveal="reveal.reveal"
293
+ />
294
+ </div>
295
+ </template>
@@ -0,0 +1,4 @@
1
+ import type { DisplayListProps } from '../../types/index.js';
2
+ declare const __VLS_export: import("vue").DefineComponent<DisplayListProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DisplayListProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
3
+ declare const _default: typeof __VLS_export;
4
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import type { DisplayMediaProps } from '../../types/index.js';
2
+ declare const __VLS_export: import("vue").DefineComponent<DisplayMediaProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DisplayMediaProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
3
+ declare const _default: typeof __VLS_export;
4
+ export default _default;
@@ -0,0 +1,326 @@
1
+ <script setup>
2
+ import UIcon from "@nuxt/ui/components/Icon.vue";
3
+ import ULink from "@nuxt/ui/components/Link.vue";
4
+ import { computed, ref } from "vue";
5
+ import { fileExtensionLabel, fileNameFromUrl, formatFileSize } from "../../../shared/utils/file";
6
+ import { isString } from "../../../shared/utils/predicate";
7
+ import { useDisplayAppUi } from "../../composables/use-display-config";
8
+ import { useDisplayFilePreview } from "../../composables/use-display-file-preview";
9
+ import { useDisplayLocale } from "../../composables/use-display-locale";
10
+ import { useDisplayUrlActions, useResolvedDisplayUrl } from "../../composables/use-display-url";
11
+ import { resolveDisplayClasses } from "../../utils/classes";
12
+ import { isDisplayContentEmpty, resolveDisplayText } from "../../utils/content";
13
+ import { downloadDisplayUrl, openDisplayUrl } from "../../utils/urls";
14
+ import DisplayActions from "../parts/display-actions.vue";
15
+ import DisplayLoadError from "../parts/display-load-error.vue";
16
+ import DisplayRender from "../parts/display-render";
17
+ import DisplaySkeleton from "../parts/display-skeleton.vue";
18
+ const {
19
+ variant = "image",
20
+ src = void 0,
21
+ alt = void 0,
22
+ caption = void 0,
23
+ images = void 0,
24
+ max = 3,
25
+ thumbnail = void 0,
26
+ title = void 0,
27
+ duration = void 0,
28
+ name = void 0,
29
+ size = void 0,
30
+ type = void 0,
31
+ pages = void 0,
32
+ href = void 0,
33
+ description = void 0,
34
+ aspect = "16 / 9",
35
+ preview = true,
36
+ actions = void 0,
37
+ loading = false,
38
+ ui = void 0
39
+ } = defineProps({
40
+ variant: { type: String, required: false },
41
+ src: { type: [String, Function], required: false },
42
+ alt: { type: String, required: false },
43
+ caption: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
44
+ images: { type: Array, required: false },
45
+ max: { type: Number, required: false },
46
+ thumbnail: { type: String, required: false },
47
+ title: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
48
+ duration: { type: [String, Number, Function], required: false },
49
+ name: { type: String, required: false },
50
+ size: { type: Number, required: false },
51
+ type: { type: String, required: false },
52
+ pages: { type: Number, required: false },
53
+ href: { type: [String, Function], required: false },
54
+ description: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
55
+ aspect: { type: String, required: false },
56
+ preview: { type: Boolean, required: false },
57
+ actions: { type: Array, required: false },
58
+ loading: { type: Boolean, required: false },
59
+ ui: { type: Object, required: false }
60
+ });
61
+ const { t, code } = useDisplayLocale();
62
+ const filePreview = useDisplayFilePreview();
63
+ const appUi = useDisplayAppUi("media");
64
+ const playing = ref(false);
65
+ const links = useDisplayUrlActions();
66
+ const shown = useResolvedDisplayUrl(
67
+ () => variant === "image" || variant === "video" || variant === "gallery" && !images ? src : void 0
68
+ );
69
+ const gallery = computed(
70
+ () => images ?? (shown.url.value ? [{ alt, src: shown.url.value }] : [])
71
+ );
72
+ const tiles = computed(() => gallery.value.slice(0, Math.max(1, max)));
73
+ const hiddenCount = computed(() => gallery.value.length - tiles.value.length);
74
+ const fileName = computed(() => name ?? (isString(src) ? fileNameFromUrl(src) : ""));
75
+ const documentMeta = computed(
76
+ () => [
77
+ fileExtensionLabel({ name: fileName.value, type }),
78
+ size === void 0 ? void 0 : formatFileSize(size, code.value),
79
+ pages === void 0 ? void 0 : t("display.pages", { count: pages })
80
+ ].filter(Boolean).join(" \xB7 ")
81
+ );
82
+ const documentActions = computed(
83
+ () => actions ?? (src ? [
84
+ {
85
+ icon: "i-lucide-eye",
86
+ label: t("display.open"),
87
+ loading: links.pending.value === "open",
88
+ onClick: openDocument
89
+ },
90
+ {
91
+ icon: "i-lucide-download",
92
+ label: t("display.download"),
93
+ loading: links.pending.value === "download",
94
+ onClick: downloadDocument
95
+ }
96
+ ] : [])
97
+ );
98
+ const domain = computed(() => {
99
+ if (!isString(href)) return "";
100
+ try {
101
+ return new URL(href).hostname.replace(/^www\./u, "");
102
+ } catch {
103
+ return href;
104
+ }
105
+ });
106
+ const galleryGrid = computed(() => {
107
+ const count = tiles.value.length;
108
+ if (count >= 3) return "grid-cols-[2fr_1fr] grid-rows-2 [&>*:first-child]:row-span-2";
109
+ return count === 2 ? "grid-cols-2" : "grid-cols-1";
110
+ });
111
+ const classes = computed(
112
+ () => resolveDisplayClasses(
113
+ {
114
+ actions: "mt-2 flex flex-wrap gap-1.5",
115
+ body: "min-w-0 flex-1",
116
+ caption: "flex items-center gap-1.5 text-xs text-muted",
117
+ description: "line-clamp-2 text-xs text-muted",
118
+ domain: "flex items-center gap-1.5 text-xs text-muted",
119
+ duration: "absolute end-2 bottom-2 rounded-[4px] bg-black/60 px-1.5 py-px text-[11px] font-semibold text-white",
120
+ frame: "relative w-full overflow-hidden rounded-[var(--nut-display-radius)] bg-elevated",
121
+ grid: `grid w-full gap-1 overflow-hidden rounded-[var(--nut-display-radius)] ${galleryGrid.value}`,
122
+ image: "size-full object-cover",
123
+ meta: "mt-0.5 text-xs text-dimmed",
124
+ name: "text-sm font-semibold break-all text-highlighted",
125
+ overflow: "absolute inset-0 grid place-items-center bg-black/50 text-base font-bold text-white",
126
+ page: "flex h-19 w-15 shrink-0 flex-col gap-1 overflow-hidden rounded-[4px] border border-default bg-white p-1.5",
127
+ play: "grid size-12 place-items-center rounded-full bg-white/90 text-black shadow-lg transition-transform group-hover:scale-105",
128
+ root: variant === "document" ? "flex min-w-0 gap-3 rounded-[var(--nut-display-radius)] border border-default p-2.5" : variant === "link" ? "flex min-w-0 overflow-hidden rounded-[var(--nut-display-radius)] border border-default transition-colors hover:bg-elevated" : "m-0 flex min-w-0 flex-col gap-1.5",
129
+ thumbnail: "w-24 shrink-0 object-cover",
130
+ tile: "relative min-h-0 overflow-hidden bg-elevated",
131
+ title: "text-sm font-semibold text-highlighted"
132
+ },
133
+ appUi.value,
134
+ ui
135
+ )
136
+ );
137
+ function openImages(index) {
138
+ if (!preview) return;
139
+ filePreview.open(
140
+ gallery.value.map((image) => ({ name: image.alt, src: image.src })),
141
+ index
142
+ );
143
+ }
144
+ function openDocument() {
145
+ if (!src) return;
146
+ if (preview && filePreview.open([{ mime: type, name: fileName.value, size, src }])) return;
147
+ void links.run("open", () => openDisplayUrl(src));
148
+ }
149
+ function downloadDocument() {
150
+ if (src) void links.run("download", () => downloadDisplayUrl(src, fileName.value || void 0));
151
+ }
152
+ function openLink() {
153
+ if (href) void links.run("open", () => openDisplayUrl(href));
154
+ }
155
+ </script>
156
+
157
+ <template>
158
+ <DisplaySkeleton
159
+ v-if="loading"
160
+ :layout="variant === 'document' || variant === 'link' ? 'card' : 'media'"
161
+ />
162
+ <figure
163
+ v-else-if="variant === 'image'"
164
+ :class="classes.root"
165
+ data-display="media"
166
+ data-variant="image"
167
+ >
168
+ <component
169
+ :is="preview && filePreview.available ? 'button' : 'div'"
170
+ :type="preview && filePreview.available ? 'button' : void 0"
171
+ :class="[classes.frame, preview && filePreview.available ? 'cursor-zoom-in' : '']"
172
+ :style="{ aspectRatio: aspect }"
173
+ @click="openImages(0)"
174
+ >
175
+ <img
176
+ v-if="shown.url.value"
177
+ :src="shown.url.value"
178
+ :alt="alt ?? ''"
179
+ :class="classes.image"
180
+ loading="lazy"
181
+ />
182
+ <span v-else-if="shown.pending.value" class="nut-dash-shimmer absolute inset-0" />
183
+ </component>
184
+ <figcaption v-if="!isDisplayContentEmpty(caption)" :class="classes.caption">
185
+ <DisplayRender :content="caption" />
186
+ </figcaption>
187
+ </figure>
188
+ <figure
189
+ v-else-if="variant === 'gallery'"
190
+ :class="classes.root"
191
+ data-display="media"
192
+ data-variant="gallery"
193
+ >
194
+ <div :class="classes.grid" :style="{ aspectRatio: aspect }">
195
+ <button
196
+ v-for="(image, index) in tiles"
197
+ :key="index"
198
+ type="button"
199
+ :class="[
200
+ classes.tile,
201
+ preview && filePreview.available ? 'cursor-zoom-in' : 'cursor-default'
202
+ ]"
203
+ @click="openImages(index)"
204
+ >
205
+ <img :src="image.src" :alt="image.alt ?? ''" :class="classes.image" loading="lazy" />
206
+ <span v-if="hiddenCount > 0 && index === tiles.length - 1" :class="classes.overflow"
207
+ >+{{ hiddenCount }}</span
208
+ >
209
+ </button>
210
+ </div>
211
+ <figcaption v-if="!isDisplayContentEmpty(caption)" :class="classes.caption">
212
+ <DisplayRender :content="caption" />
213
+ </figcaption>
214
+ </figure>
215
+ <figure
216
+ v-else-if="variant === 'video'"
217
+ :class="classes.root"
218
+ data-display="media"
219
+ data-variant="video"
220
+ >
221
+ <div :class="[classes.frame, 'bg-black']" :style="{ aspectRatio: aspect }">
222
+ <video
223
+ v-if="playing || !thumbnail"
224
+ :src="shown.url.value"
225
+ :poster="thumbnail"
226
+ :autoplay="playing"
227
+ controls
228
+ preload="metadata"
229
+ :class="classes.image"
230
+ />
231
+ <button
232
+ v-else
233
+ type="button"
234
+ class="group absolute inset-0 grid cursor-pointer place-items-center"
235
+ :aria-label="t('display.play')"
236
+ @click="playing = true"
237
+ >
238
+ <img :src="thumbnail" alt="" :class="[classes.image, 'absolute inset-0']" />
239
+ <span class="absolute inset-0 bg-gradient-to-t from-black/55 to-transparent to-45%" />
240
+ <span :class="[classes.play, 'relative']"
241
+ ><UIcon name="i-lucide-play" class="size-5 translate-x-px"
242
+ /></span>
243
+ <span
244
+ v-if="!isDisplayContentEmpty(title)"
245
+ :class="[classes.title, 'absolute start-3 bottom-2 text-white']"
246
+ >
247
+ <DisplayRender :content="title" />
248
+ </span>
249
+ <span v-if="duration !== void 0" :class="classes.duration">{{
250
+ resolveDisplayText(duration)
251
+ }}</span>
252
+ </button>
253
+ </div>
254
+ <figcaption v-if="!isDisplayContentEmpty(caption)" :class="classes.caption">
255
+ <DisplayRender :content="caption" />
256
+ </figcaption>
257
+ </figure>
258
+ <div
259
+ v-else-if="variant === 'document'"
260
+ :class="classes.root"
261
+ data-display="media"
262
+ data-variant="document"
263
+ >
264
+ <img
265
+ v-if="thumbnail"
266
+ :src="thumbnail"
267
+ alt=""
268
+ :class="[classes.page, 'p-0 object-cover object-top']"
269
+ />
270
+ <div v-else :class="classes.page" aria-hidden="true">
271
+ <i class="mb-0.5 h-1 w-3/5 rounded-full bg-[var(--nut-display-primary)]" />
272
+ <i
273
+ v-for="width in [100, 92, 100, 78, 96, 60]"
274
+ :key="width"
275
+ class="h-0.5 rounded-full bg-[#e5e1db]"
276
+ :style="{ width: `${width}%` }"
277
+ />
278
+ </div>
279
+ <div :class="classes.body">
280
+ <div :class="classes.name">{{ fileName }}</div>
281
+ <div v-if="documentMeta" :class="classes.meta">{{ documentMeta }}</div>
282
+ <div v-if="!isDisplayContentEmpty(description)" :class="classes.description">
283
+ <DisplayRender :content="description" />
284
+ </div>
285
+ <DisplayActions
286
+ v-if="documentActions.length"
287
+ :actions="documentActions"
288
+ size="xs"
289
+ :class="classes.actions"
290
+ />
291
+ </div>
292
+ </div>
293
+ <component
294
+ :is="isString(href) ? ULink : 'button'"
295
+ v-else
296
+ v-bind="isString(href) ? { target: '_blank', to: href } : { type: 'button' }"
297
+ :class="[classes.root, isString(href) ? '' : 'w-full cursor-pointer text-start']"
298
+ :aria-busy="links.pending.value === 'open' || void 0"
299
+ data-display="media"
300
+ data-variant="link"
301
+ @click="isString(href) ? void 0 : openLink()"
302
+ >
303
+ <div :class="[classes.body, 'px-3 py-2.5']">
304
+ <div v-if="domain" :class="classes.domain">
305
+ <UIcon name="i-lucide-globe" class="size-3.5 shrink-0" />{{ domain }}
306
+ </div>
307
+ <div :class="[classes.title, 'mt-0.5 flex items-center gap-1.5']">
308
+ <DisplayRender :content="title ?? (isString(href) ? href : void 0)" />
309
+ <UIcon
310
+ v-if="links.pending.value === 'open'"
311
+ name="i-lucide-loader-circle"
312
+ class="size-3.5 shrink-0 animate-spin text-muted"
313
+ />
314
+ </div>
315
+ <div v-if="!isDisplayContentEmpty(description)" :class="classes.description">
316
+ <DisplayRender :content="description" />
317
+ </div>
318
+ </div>
319
+ <img v-if="thumbnail" :src="thumbnail" alt="" :class="classes.thumbnail" loading="lazy" />
320
+ </component>
321
+ <DisplayLoadError
322
+ v-if="!loading && (links.failed.value || shown.failed.value)"
323
+ class="mt-1.5"
324
+ @retry="links.failed.value ? links.failed.value.retry() : shown.retry()"
325
+ />
326
+ </template>
@@ -0,0 +1,4 @@
1
+ import type { DisplayMediaProps } from '../../types/index.js';
2
+ declare const __VLS_export: import("vue").DefineComponent<DisplayMediaProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DisplayMediaProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
3
+ declare const _default: typeof __VLS_export;
4
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import type { DisplayMeterProps } from '../../types/index.js';
2
+ declare const __VLS_export: import("vue").DefineComponent<DisplayMeterProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DisplayMeterProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
3
+ declare const _default: typeof __VLS_export;
4
+ export default _default;