nuxt-ui-tools 1.24.0 → 1.25.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.
Files changed (210) 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 +96 -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 +136 -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 +184 -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 +241 -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 +144 -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 +179 -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 +154 -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 +75 -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 +195 -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 +103 -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 +301 -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 +336 -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 +302 -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 +153 -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 +268 -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 +189 -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 +136 -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 +141 -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 +238 -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 +27 -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 +71 -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 +36 -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 +11 -0
  86. package/dist/runtime/display/components/parts/display-record-media.vue +61 -0
  87. package/dist/runtime/display/components/parts/display-record-media.vue.d.ts +11 -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/directives/reveal-truncated.d.ts +10 -0
  108. package/dist/runtime/display/directives/reveal-truncated.js +31 -0
  109. package/dist/runtime/display/index.d.ts +4 -0
  110. package/dist/runtime/display/index.js +3 -0
  111. package/dist/runtime/display/plugins/display-tones.d.ts +3 -0
  112. package/dist/runtime/display/plugins/display-tones.js +17 -0
  113. package/dist/runtime/display/types/blocks/accordion.d.ts +18 -0
  114. package/dist/runtime/display/types/blocks/accordion.js +0 -0
  115. package/dist/runtime/display/types/blocks/alert.d.ts +31 -0
  116. package/dist/runtime/display/types/blocks/alert.js +0 -0
  117. package/dist/runtime/display/types/blocks/breakdown.d.ts +36 -0
  118. package/dist/runtime/display/types/blocks/breakdown.js +0 -0
  119. package/dist/runtime/display/types/blocks/chart.d.ts +23 -0
  120. package/dist/runtime/display/types/blocks/chart.js +0 -0
  121. package/dist/runtime/display/types/blocks/code.d.ts +21 -0
  122. package/dist/runtime/display/types/blocks/code.js +0 -0
  123. package/dist/runtime/display/types/blocks/date.d.ts +27 -0
  124. package/dist/runtime/display/types/blocks/date.js +0 -0
  125. package/dist/runtime/display/types/blocks/diff.d.ts +20 -0
  126. package/dist/runtime/display/types/blocks/diff.js +0 -0
  127. package/dist/runtime/display/types/blocks/empty.d.ts +13 -0
  128. package/dist/runtime/display/types/blocks/empty.js +0 -0
  129. package/dist/runtime/display/types/blocks/facts.d.ts +52 -0
  130. package/dist/runtime/display/types/blocks/facts.js +0 -0
  131. package/dist/runtime/display/types/blocks/heading.d.ts +19 -0
  132. package/dist/runtime/display/types/blocks/heading.js +0 -0
  133. package/dist/runtime/display/types/blocks/list.d.ts +36 -0
  134. package/dist/runtime/display/types/blocks/list.js +0 -0
  135. package/dist/runtime/display/types/blocks/media.d.ts +42 -0
  136. package/dist/runtime/display/types/blocks/media.js +0 -0
  137. package/dist/runtime/display/types/blocks/meter.d.ts +48 -0
  138. package/dist/runtime/display/types/blocks/meter.js +0 -0
  139. package/dist/runtime/display/types/blocks/person.d.ts +34 -0
  140. package/dist/runtime/display/types/blocks/person.js +0 -0
  141. package/dist/runtime/display/types/blocks/record.d.ts +44 -0
  142. package/dist/runtime/display/types/blocks/record.js +0 -0
  143. package/dist/runtime/display/types/blocks/stat.d.ts +44 -0
  144. package/dist/runtime/display/types/blocks/stat.js +0 -0
  145. package/dist/runtime/display/types/blocks/table.d.ts +28 -0
  146. package/dist/runtime/display/types/blocks/table.js +0 -0
  147. package/dist/runtime/display/types/blocks/text.d.ts +22 -0
  148. package/dist/runtime/display/types/blocks/text.js +0 -0
  149. package/dist/runtime/display/types/blocks/timeline.d.ts +37 -0
  150. package/dist/runtime/display/types/blocks/timeline.js +0 -0
  151. package/dist/runtime/display/types/config.d.ts +60 -0
  152. package/dist/runtime/display/types/config.js +0 -0
  153. package/dist/runtime/display/types/index.d.ts +22 -0
  154. package/dist/runtime/display/types/index.js +0 -0
  155. package/dist/runtime/display/types/registry.d.ts +57 -0
  156. package/dist/runtime/display/types/registry.js +0 -0
  157. package/dist/runtime/display/types/shared.d.ts +80 -0
  158. package/dist/runtime/display/types/shared.js +0 -0
  159. package/dist/runtime/display/utils/classes.d.ts +3 -0
  160. package/dist/runtime/display/utils/classes.js +7 -0
  161. package/dist/runtime/display/utils/config.d.ts +11 -0
  162. package/dist/runtime/display/utils/config.js +17 -0
  163. package/dist/runtime/display/utils/content.d.ts +7 -0
  164. package/dist/runtime/display/utils/content.js +13 -0
  165. package/dist/runtime/display/utils/dates.d.ts +16 -0
  166. package/dist/runtime/display/utils/dates.js +34 -0
  167. package/dist/runtime/display/utils/json.d.ts +7 -0
  168. package/dist/runtime/display/utils/json.js +15 -0
  169. package/dist/runtime/display/utils/links.d.ts +12 -0
  170. package/dist/runtime/display/utils/links.js +9 -0
  171. package/dist/runtime/display/utils/meter.d.ts +7 -0
  172. package/dist/runtime/display/utils/meter.js +13 -0
  173. package/dist/runtime/display/utils/registry.d.ts +47 -0
  174. package/dist/runtime/display/utils/registry.js +64 -0
  175. package/dist/runtime/display/utils/tone-css.d.ts +7 -0
  176. package/dist/runtime/display/utils/tone-css.js +38 -0
  177. package/dist/runtime/display/utils/tone.d.ts +26 -0
  178. package/dist/runtime/display/utils/tone.js +38 -0
  179. package/dist/runtime/display/utils/urls.d.ts +10 -0
  180. package/dist/runtime/display/utils/urls.js +33 -0
  181. package/dist/runtime/file-preview/components/shell/file-preview-stage.vue +5 -3
  182. package/dist/runtime/file-preview/composables/use-file-preview-source.d.ts +7 -0
  183. package/dist/runtime/file-preview/composables/use-file-preview-source.js +7 -1
  184. package/dist/runtime/file-preview/index.d.ts +1 -0
  185. package/dist/runtime/file-preview/index.js +1 -0
  186. package/dist/runtime/file-preview/types/file.d.ts +9 -0
  187. package/dist/runtime/file-preview/utils/errors.d.ts +30 -0
  188. package/dist/runtime/file-preview/utils/errors.js +26 -0
  189. package/dist/runtime/form/components/utils/form-file-preview.vue +7 -2
  190. package/dist/runtime/form/composables/use-form-root.js +17 -4
  191. package/dist/runtime/form/composables/use-form-runtime.js +7 -2
  192. package/dist/runtime/form/fields/index.d.ts +5 -0
  193. package/dist/runtime/form/fields/info/component.vue +51 -19
  194. package/dist/runtime/form/fields/info/config.d.ts +5 -0
  195. package/dist/runtime/form/fields/info/config.js +2 -1
  196. package/dist/runtime/form/fields/info/types.d.ts +78 -4
  197. package/dist/runtime/form/fields/info/use-info-display.d.ts +20 -0
  198. package/dist/runtime/form/fields/info/use-info-display.js +82 -0
  199. package/dist/runtime/form/fields/upload/upload-item.vue +10 -2
  200. package/dist/runtime/form/types/api.d.ts +6 -0
  201. package/dist/runtime/form/types/runtime.d.ts +2 -0
  202. package/dist/runtime/form/utils/dynamic.d.ts +6 -0
  203. package/dist/runtime/form/utils/dynamic.js +7 -0
  204. package/dist/runtime/i18n/locales/en.js +27 -0
  205. package/dist/runtime/i18n/locales/fr.js +27 -0
  206. package/dist/runtime/i18n/types.d.ts +35 -0
  207. package/dist/runtime/shared/styles/tokens.css +1 -1
  208. package/dist/runtime/shared/utils/file.d.ts +1 -1
  209. package/dist/runtime/table/utils/data-list-ui.d.ts +1 -1
  210. package/package.json +16 -1
@@ -0,0 +1,302 @@
1
+ <script setup>
2
+ import UIcon from "@nuxt/ui/components/Icon.vue";
3
+ import { computed } from "vue";
4
+ import { isPlainObject } from "../../../shared/utils/predicate";
5
+ import { useDisplayAppUi } from "../../composables/use-display-config";
6
+ import { useDisplayNumber } from "../../composables/use-display-number";
7
+ import { vRevealTruncated } from "../../directives/reveal-truncated";
8
+ import { resolveDisplayClasses } from "../../utils/classes";
9
+ import { displayContentText, isDisplayContentEmpty, resolveDisplayText } from "../../utils/content";
10
+ import { meterShare, meterTone } from "../../utils/meter";
11
+ import { displayToneClass } from "../../utils/tone";
12
+ import DisplayRender from "../parts/display-render";
13
+ import DisplaySkeleton from "../parts/display-skeleton.vue";
14
+ const {
15
+ variant = "bar",
16
+ label = void 0,
17
+ value = 0,
18
+ max = void 0,
19
+ valueLabel = void 0,
20
+ tone = void 0,
21
+ thresholds = void 0,
22
+ parts = void 0,
23
+ caption = void 0,
24
+ levels = void 0,
25
+ current = void 0,
26
+ target = void 0,
27
+ size = "md",
28
+ loading = false,
29
+ ui = void 0
30
+ } = defineProps({
31
+ variant: { type: String, required: false },
32
+ label: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
33
+ value: { type: Number, required: false },
34
+ max: { type: Number, required: false },
35
+ valueLabel: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
36
+ tone: { type: String, required: false },
37
+ thresholds: { type: Object, required: false },
38
+ parts: { type: Array, required: false },
39
+ caption: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
40
+ levels: { type: Array, required: false },
41
+ current: { type: Number, required: false },
42
+ target: { type: Number, required: false },
43
+ size: { type: String, required: false },
44
+ loading: { type: Boolean, required: false },
45
+ ui: { type: Object, required: false }
46
+ });
47
+ const PALETTE = ["primary", "info", "success", "warning", "secondary", "neutral"];
48
+ const RING_SIZES = { lg: 112, md: 88, sm: 64 };
49
+ const format = useDisplayNumber();
50
+ const appUi = useDisplayAppUi("meter");
51
+ const partTotal = computed(() => (parts ?? []).reduce((sum, part) => sum + part.value, 0));
52
+ const resolvedMax = computed(
53
+ () => max ?? (variant === "segments" || variant === "split" ? partTotal.value : 100)
54
+ );
55
+ const share = computed(() => meterShare(value, resolvedMax.value));
56
+ const resolvedTone = computed(() => meterTone(share.value, tone, thresholds));
57
+ const resolvedParts = computed(
58
+ () => (parts ?? []).map((part, index) => ({
59
+ ...part,
60
+ tone: part.tone ?? (variant === "ring" ? resolvedTone.value : PALETTE[index % PALETTE.length] ?? "primary"),
61
+ width: meterShare(part.value, resolvedMax.value) * 100
62
+ }))
63
+ );
64
+ const headerValue = computed(() => valueLabel ?? `${format(value)} / ${format(resolvedMax.value)}`);
65
+ const ariaLabel = computed(() => displayContentText(label));
66
+ const ring = computed(() => {
67
+ const diameter = RING_SIZES[size];
68
+ const stroke = Math.round(diameter / 11);
69
+ const radius = (diameter - stroke) / 2;
70
+ const circumference = 2 * Math.PI * radius;
71
+ return { circumference, diameter, radius, stroke, filled: circumference * share.value };
72
+ });
73
+ const resolvedLevels = computed(
74
+ () => (levels ?? []).map(
75
+ (level) => isMeterLevel(level) ? level : { label: level }
76
+ )
77
+ );
78
+ const classes = computed(
79
+ () => resolveDisplayClasses(
80
+ {
81
+ bar: "h-full bg-[var(--nut-tone)] transition-[width] duration-300 ease-out first:rounded-s-full last:rounded-e-full",
82
+ caption: "mt-1.5 text-xs text-dimmed",
83
+ header: "flex items-baseline justify-between gap-3 text-sm",
84
+ label: "flex min-w-0 items-center gap-1.5 font-semibold text-toned",
85
+ legend: "mt-2 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted",
86
+ legendItem: "inline-flex items-center gap-1.5",
87
+ level: "grid h-8 place-items-center rounded-[calc(var(--nut-display-radius)*0.75)] text-xs font-bold",
88
+ levelCaption: "text-center text-[10.5px] text-dimmed",
89
+ levels: "mt-2 grid gap-1",
90
+ parts: "flex min-w-0 flex-1 flex-col gap-2.5",
91
+ ring: "flex items-center gap-4",
92
+ root: "min-w-0",
93
+ track: "mt-2 flex h-2 gap-0.5 overflow-hidden rounded-full bg-[var(--nut-display-track)]",
94
+ value: "shrink-0 font-semibold text-highlighted tabular-nums"
95
+ },
96
+ appUi.value,
97
+ ui
98
+ )
99
+ );
100
+ function levelClass(index) {
101
+ if (index === current) return "bg-[var(--nut-tone)] text-[var(--nut-tone-contrast)]";
102
+ if (index === target)
103
+ return "text-[var(--nut-tone-ink)] outline-dashed outline-[1.5px] -outline-offset-[1.5px] outline-[var(--nut-tone)]";
104
+ if (current !== void 0 && index < current)
105
+ return "bg-[var(--nut-tone-soft)] text-[var(--nut-tone-ink)]";
106
+ return "bg-elevated text-dimmed";
107
+ }
108
+ function isMeterLevel(level) {
109
+ return isPlainObject(level);
110
+ }
111
+ function partTone(partToneName) {
112
+ return displayToneClass(partToneName);
113
+ }
114
+ </script>
115
+
116
+ <template>
117
+ <DisplaySkeleton v-if="loading" :layout="variant === 'ring' ? 'ring' : 'lines'" :count="2" />
118
+ <div
119
+ v-else
120
+ :class="[classes.root, displayToneClass(resolvedTone)]"
121
+ data-display="meter"
122
+ :data-variant="variant"
123
+ >
124
+ <div v-if="variant === 'ring'" :class="classes.ring">
125
+ <svg
126
+ :width="ring.diameter"
127
+ :height="ring.diameter"
128
+ :viewBox="`0 0 ${ring.diameter} ${ring.diameter}`"
129
+ class="shrink-0"
130
+ role="meter"
131
+ :aria-valuenow="value"
132
+ aria-valuemin="0"
133
+ :aria-valuemax="resolvedMax"
134
+ :aria-label="ariaLabel"
135
+ >
136
+ <circle
137
+ :cx="ring.diameter / 2"
138
+ :cy="ring.diameter / 2"
139
+ :r="ring.radius"
140
+ fill="none"
141
+ stroke="var(--nut-display-track)"
142
+ :stroke-width="ring.stroke"
143
+ />
144
+ <circle
145
+ :cx="ring.diameter / 2"
146
+ :cy="ring.diameter / 2"
147
+ :r="ring.radius"
148
+ fill="none"
149
+ stroke="var(--nut-tone)"
150
+ :stroke-width="ring.stroke"
151
+ stroke-linecap="round"
152
+ :stroke-dasharray="`${ring.filled} ${ring.circumference}`"
153
+ :transform="`rotate(-90 ${ring.diameter / 2} ${ring.diameter / 2})`"
154
+ />
155
+ <text
156
+ :x="ring.diameter / 2"
157
+ :y="ring.diameter / 2 + ring.diameter * 0.04"
158
+ text-anchor="middle"
159
+ class="fill-[var(--ui-text-highlighted)] font-extrabold"
160
+ :font-size="ring.diameter * 0.22"
161
+ >
162
+ {{ displayContentText(valueLabel) ?? format(value) }}
163
+ </text>
164
+ <text
165
+ :x="ring.diameter / 2"
166
+ :y="ring.diameter / 2 + ring.diameter * 0.2"
167
+ text-anchor="middle"
168
+ class="fill-[var(--ui-text-dimmed)]"
169
+ :font-size="ring.diameter * 0.115"
170
+ >
171
+ / {{ format(resolvedMax) }}
172
+ </text>
173
+ </svg>
174
+ <div :class="classes.parts">
175
+ <div v-if="!isDisplayContentEmpty(label)" v-reveal-truncated :class="classes.label">
176
+ <DisplayRender :content="label" />
177
+ </div>
178
+ <div v-for="(part, index) in resolvedParts" :key="index" :class="partTone(part.tone)">
179
+ <div :class="classes.header">
180
+ <span v-reveal-truncated :class="classes.label">
181
+ <UIcon v-if="part.icon" :name="part.icon" class="size-3.5" />
182
+ <DisplayRender :content="part.label" />
183
+ </span>
184
+ <span v-reveal-truncated :class="classes.value"
185
+ ><DisplayRender :content="part.valueLabel ?? format(part.value)"
186
+ /></span>
187
+ </div>
188
+ <div :class="[classes.track, 'mt-1 h-1.5']">
189
+ <i
190
+ :class="classes.bar"
191
+ :style="{ width: `${meterShare(part.value, part.max ?? resolvedMax) * 100}%` }"
192
+ />
193
+ </div>
194
+ </div>
195
+ <div v-if="!isDisplayContentEmpty(caption)" v-reveal-truncated :class="classes.caption">
196
+ <DisplayRender :content="caption" />
197
+ </div>
198
+ </div>
199
+ </div>
200
+ <template v-else-if="variant === 'scale'">
201
+ <div :class="classes.header">
202
+ <span v-reveal-truncated :class="classes.label"><DisplayRender :content="label" /></span>
203
+ <span v-if="!isDisplayContentEmpty(valueLabel)" v-reveal-truncated :class="classes.value"
204
+ ><DisplayRender :content="valueLabel"
205
+ /></span>
206
+ </div>
207
+ <div
208
+ :class="classes.levels"
209
+ :style="{ gridTemplateColumns: `repeat(${resolvedLevels.length || 1}, minmax(0, 1fr))` }"
210
+ >
211
+ <div
212
+ v-for="(level, index) in resolvedLevels"
213
+ :key="index"
214
+ :class="[classes.level, levelClass(index)]"
215
+ :data-state="
216
+ index === current ? 'current' : index === target ? 'target' : current !== void 0 && index < current ? 'reached' : 'idle'
217
+ "
218
+ >
219
+ {{ resolveDisplayText(level.label) }}
220
+ </div>
221
+ </div>
222
+ <div
223
+ v-if="resolvedLevels.some((level2) => level2.caption !== void 0)"
224
+ class="mt-1 grid gap-1"
225
+ :style="{ gridTemplateColumns: `repeat(${resolvedLevels.length}, minmax(0, 1fr))` }"
226
+ >
227
+ <span v-for="(level, index) in resolvedLevels" :key="index" :class="classes.levelCaption">
228
+ {{ resolveDisplayText(level.caption) }}
229
+ </span>
230
+ </div>
231
+ <div v-if="!isDisplayContentEmpty(caption)" v-reveal-truncated :class="classes.caption">
232
+ <DisplayRender :content="caption" />
233
+ </div>
234
+ </template>
235
+ <template v-else-if="variant === 'split'">
236
+ <div class="mb-1.5 flex items-end justify-between gap-3 text-xs">
237
+ <span
238
+ v-for="(part, index) in resolvedParts.slice(0, 2)"
239
+ :key="index"
240
+ :class="['flex items-baseline gap-1.5', index === 1 ? 'text-end' : '']"
241
+ >
242
+ <b class="text-base font-extrabold text-highlighted tabular-nums"
243
+ >{{ Math.round(part.width) }} %</b
244
+ >
245
+ <span class="text-muted"><DisplayRender :content="part.label" /></span>
246
+ </span>
247
+ </div>
248
+ <div :class="[classes.track, 'mt-0']" role="img" :aria-label="ariaLabel">
249
+ <i
250
+ v-for="(part, index) in resolvedParts.slice(0, 2)"
251
+ :key="index"
252
+ :class="[classes.bar, partTone(part.tone)]"
253
+ :style="{ width: `${part.width}%` }"
254
+ />
255
+ </div>
256
+ <div v-if="!isDisplayContentEmpty(caption)" v-reveal-truncated :class="classes.caption">
257
+ <DisplayRender :content="caption" />
258
+ </div>
259
+ </template>
260
+ <template v-else>
261
+ <div v-if="!isDisplayContentEmpty(label) || variant === 'bar'" :class="classes.header">
262
+ <span v-reveal-truncated :class="classes.label"><DisplayRender :content="label" /></span>
263
+ <span v-reveal-truncated :class="classes.value"
264
+ ><DisplayRender :content="variant === 'bar' ? headerValue : valueLabel"
265
+ /></span>
266
+ </div>
267
+ <div
268
+ :class="classes.track"
269
+ role="meter"
270
+ :aria-valuenow="variant === 'bar' ? value : partTotal"
271
+ aria-valuemin="0"
272
+ :aria-valuemax="resolvedMax"
273
+ :aria-label="ariaLabel"
274
+ >
275
+ <i v-if="variant === 'bar'" :class="classes.bar" :style="{ width: `${share * 100}%` }" />
276
+ <i
277
+ v-for="(part, index) in variant === 'segments' ? resolvedParts : []"
278
+ v-else
279
+ :key="index"
280
+ :class="[classes.bar, partTone(part.tone)]"
281
+ :style="{ width: `${part.width}%` }"
282
+ />
283
+ </div>
284
+ <div v-if="variant === 'segments' && resolvedParts.length" :class="classes.legend">
285
+ <span
286
+ v-for="(part, index) in resolvedParts"
287
+ :key="index"
288
+ :class="[classes.legendItem, partTone(part.tone)]"
289
+ >
290
+ <i class="size-2 rounded-[2px] bg-[var(--nut-tone)]" />
291
+ <DisplayRender :content="part.label" />
292
+ <b class="font-semibold text-default tabular-nums"
293
+ ><DisplayRender :content="part.valueLabel ?? format(part.value)"
294
+ /></b>
295
+ </span>
296
+ </div>
297
+ <div v-if="!isDisplayContentEmpty(caption)" v-reveal-truncated :class="classes.caption">
298
+ <DisplayRender :content="caption" />
299
+ </div>
300
+ </template>
301
+ </div>
302
+ </template>
@@ -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;
@@ -0,0 +1,4 @@
1
+ import type { DisplayPersonProps } from '../../types/index.js';
2
+ declare const __VLS_export: import("vue").DefineComponent<DisplayPersonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DisplayPersonProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
3
+ declare const _default: typeof __VLS_export;
4
+ export default _default;
@@ -0,0 +1,153 @@
1
+ <script setup>
2
+ import UAvatar from "@nuxt/ui/components/Avatar.vue";
3
+ import UIcon from "@nuxt/ui/components/Icon.vue";
4
+ import ULink from "@nuxt/ui/components/Link.vue";
5
+ import { computed } from "vue";
6
+ import { useDisplayAppUi } from "../../composables/use-display-config";
7
+ import { vRevealTruncated } from "../../directives/reveal-truncated";
8
+ import { resolveDisplayClasses } from "../../utils/classes";
9
+ import { isDisplayContentEmpty, resolveDisplayText } from "../../utils/content";
10
+ import { displayLinkAttrs } from "../../utils/links";
11
+ import {
12
+ DISPLAY_SURFACE_CLASSES,
13
+ displayToneClass,
14
+ initialsOf,
15
+ toneFromName
16
+ } from "../../utils/tone";
17
+ import DisplayChip from "../parts/display-chip.vue";
18
+ import DisplayCopy from "../parts/display-copy.vue";
19
+ import DisplayRender from "../parts/display-render";
20
+ import DisplaySkeleton from "../parts/display-skeleton.vue";
21
+ const {
22
+ variant = "user",
23
+ people = void 0,
24
+ max = 4,
25
+ label = void 0,
26
+ contacts = void 0,
27
+ size = "md",
28
+ loading = false,
29
+ ui = void 0,
30
+ ...person
31
+ } = defineProps({
32
+ variant: { type: String, required: false },
33
+ people: { type: Array, required: false },
34
+ max: { type: Number, required: false },
35
+ label: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
36
+ contacts: { type: Array, required: false },
37
+ size: { type: String, required: false },
38
+ loading: { type: Boolean, required: false },
39
+ ui: { type: Object, required: false },
40
+ name: { type: [String, Number, Function], required: false },
41
+ description: { type: [String, Number, Function, Object, Boolean, null, Array], required: false, skipCheck: true },
42
+ avatar: { type: Object, required: false },
43
+ tone: { type: String, required: false },
44
+ chip: { type: Object, required: false }
45
+ });
46
+ const AVATAR_SIZES = {
47
+ contact: { lg: "2xl", md: "xl", sm: "lg" },
48
+ group: { lg: "lg", md: "md", sm: "sm" },
49
+ user: { lg: "xl", md: "lg", sm: "md" }
50
+ };
51
+ const appUi = useDisplayAppUi("person");
52
+ const self = computed(() => ({ ...person, name: person.name ?? "" }));
53
+ const shown = computed(() => (people ?? []).slice(0, max));
54
+ const overflow = computed(() => Math.max(0, (people?.length ?? 0) - shown.value.length));
55
+ const classes = computed(
56
+ () => resolveDisplayClasses(
57
+ {
58
+ avatar: variant === "group" ? "ring-2 ring-[var(--ui-bg)]" : "",
59
+ body: "min-w-0 flex-1",
60
+ chip: "shrink-0",
61
+ contact: "flex min-w-0 items-center gap-2 text-sm text-toned",
62
+ contactIcon: "size-3.5 shrink-0 text-muted",
63
+ contactValue: "min-w-0 flex-1 truncate",
64
+ contacts: "mt-3 flex flex-col gap-1",
65
+ description: "truncate text-xs text-muted",
66
+ group: "flex -space-x-2",
67
+ label: "text-sm text-toned",
68
+ name: `truncate font-semibold text-highlighted ${size === "lg" ? "text-base" : "text-sm"}`,
69
+ root: variant === "contact" ? `min-w-0 ${DISPLAY_SURFACE_CLASSES.bordered} p-3.5` : "flex min-w-0 items-center gap-3"
70
+ },
71
+ appUi.value,
72
+ ui
73
+ )
74
+ );
75
+ function avatarOf(entry) {
76
+ const name = resolveDisplayText(entry.name) ?? "";
77
+ const tone = entry.tone ?? toneFromName(name);
78
+ return {
79
+ alt: name,
80
+ text: initialsOf(name),
81
+ ...entry.avatar,
82
+ class: [displayToneClass(tone), classes.value.avatar, "shrink-0"],
83
+ ui: {
84
+ fallback: "text-[0.8em] font-semibold text-[var(--nut-tone-ink)]",
85
+ root: "bg-[var(--nut-tone-soft)]"
86
+ }
87
+ };
88
+ }
89
+ </script>
90
+
91
+ <template>
92
+ <DisplaySkeleton v-if="loading" layout="card" />
93
+ <div
94
+ v-else-if="variant === 'group'"
95
+ :class="classes.root"
96
+ data-display="person"
97
+ data-variant="group"
98
+ >
99
+ <div :class="classes.group">
100
+ <UAvatar
101
+ v-for="(person, index) in shown"
102
+ :key="index"
103
+ v-bind="avatarOf(person)"
104
+ :size="AVATAR_SIZES.group[size]"
105
+ :title="resolveDisplayText(person.name)"
106
+ />
107
+ <UAvatar
108
+ v-if="overflow"
109
+ :text="`+${overflow}`"
110
+ :size="AVATAR_SIZES.group[size]"
111
+ :class="[classes.avatar, 'nut-display-tone-neutral']"
112
+ :ui="{ fallback: 'font-semibold text-toned', root: 'bg-elevated' }"
113
+ />
114
+ </div>
115
+ <span v-if="!isDisplayContentEmpty(label)" v-reveal-truncated :class="classes.label"
116
+ ><DisplayRender :content="label"
117
+ /></span>
118
+ </div>
119
+ <div v-else :class="classes.root" data-display="person" :data-variant="variant">
120
+ <div class="flex min-w-0 items-center gap-3">
121
+ <UAvatar
122
+ v-bind="avatarOf(self)"
123
+ :size="AVATAR_SIZES[variant === 'contact' ? 'contact' : 'user'][size]"
124
+ />
125
+ <div :class="classes.body">
126
+ <div v-reveal-truncated :class="classes.name">{{ resolveDisplayText(self.name) }}</div>
127
+ <div
128
+ v-if="!isDisplayContentEmpty(self.description)"
129
+ v-reveal-truncated
130
+ :class="classes.description"
131
+ >
132
+ <DisplayRender :content="self.description" />
133
+ </div>
134
+ </div>
135
+ <DisplayChip v-if="self.chip" :chip="self.chip" :class="classes.chip" />
136
+ </div>
137
+ <div v-if="variant === 'contact' && contacts?.length" :class="classes.contacts">
138
+ <div v-for="(contact, index) in contacts" :key="index" :class="classes.contact">
139
+ <UIcon v-if="contact.icon" :name="contact.icon" :class="classes.contactIcon" />
140
+ <ULink
141
+ v-if="contact.href"
142
+ v-bind="displayLinkAttrs(contact.href, contact.target)"
143
+ v-reveal-truncated
144
+ :class="[classes.contactValue, 'hover:text-highlighted']"
145
+ >
146
+ {{ contact.value }}
147
+ </ULink>
148
+ <span v-else v-reveal-truncated :class="classes.contactValue">{{ contact.value }}</span>
149
+ <DisplayCopy v-if="contact.copy !== false" :value="contact.value" class="-my-1" />
150
+ </div>
151
+ </div>
152
+ </div>
153
+ </template>
@@ -0,0 +1,4 @@
1
+ import type { DisplayPersonProps } from '../../types/index.js';
2
+ declare const __VLS_export: import("vue").DefineComponent<DisplayPersonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DisplayPersonProps> & 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 { DisplayRecordProps } from '../../types/index.js';
2
+ declare const __VLS_export: import("vue").DefineComponent<DisplayRecordProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DisplayRecordProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
3
+ declare const _default: typeof __VLS_export;
4
+ export default _default;