@iris-ui-kit/vue 0.1.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 (164) hide show
  1. package/LICENSE +21 -0
  2. package/dist/admin.cjs +1869 -0
  3. package/dist/admin.cjs.map +1 -0
  4. package/dist/admin.d.cts +376 -0
  5. package/dist/admin.d.ts +376 -0
  6. package/dist/admin.js +10 -0
  7. package/dist/admin.js.map +1 -0
  8. package/dist/async.cjs +76 -0
  9. package/dist/async.cjs.map +1 -0
  10. package/dist/async.d.cts +59 -0
  11. package/dist/async.d.ts +59 -0
  12. package/dist/async.js +3 -0
  13. package/dist/async.js.map +1 -0
  14. package/dist/behaviors.cjs +659 -0
  15. package/dist/behaviors.cjs.map +1 -0
  16. package/dist/behaviors.d.cts +441 -0
  17. package/dist/behaviors.d.ts +441 -0
  18. package/dist/behaviors.js +3 -0
  19. package/dist/behaviors.js.map +1 -0
  20. package/dist/chunk-3FJUCHCC.js +146 -0
  21. package/dist/chunk-3FJUCHCC.js.map +1 -0
  22. package/dist/chunk-3R3QDLJC.js +172 -0
  23. package/dist/chunk-3R3QDLJC.js.map +1 -0
  24. package/dist/chunk-4FLIW67H.js +137 -0
  25. package/dist/chunk-4FLIW67H.js.map +1 -0
  26. package/dist/chunk-4FQ7XF52.js +3 -0
  27. package/dist/chunk-4FQ7XF52.js.map +1 -0
  28. package/dist/chunk-72PZMU6R.js +99 -0
  29. package/dist/chunk-72PZMU6R.js.map +1 -0
  30. package/dist/chunk-75GSYEJS.js +17 -0
  31. package/dist/chunk-75GSYEJS.js.map +1 -0
  32. package/dist/chunk-7IOJADZC.js +38 -0
  33. package/dist/chunk-7IOJADZC.js.map +1 -0
  34. package/dist/chunk-CIZGSGXN.js +651 -0
  35. package/dist/chunk-CIZGSGXN.js.map +1 -0
  36. package/dist/chunk-F77KIPOH.js +103 -0
  37. package/dist/chunk-F77KIPOH.js.map +1 -0
  38. package/dist/chunk-FJBYAIL4.js +1318 -0
  39. package/dist/chunk-FJBYAIL4.js.map +1 -0
  40. package/dist/chunk-H7HVVPYR.js +66 -0
  41. package/dist/chunk-H7HVVPYR.js.map +1 -0
  42. package/dist/chunk-I6D5VXQK.js +167 -0
  43. package/dist/chunk-I6D5VXQK.js.map +1 -0
  44. package/dist/chunk-PPYNXNGZ.js +73 -0
  45. package/dist/chunk-PPYNXNGZ.js.map +1 -0
  46. package/dist/chunk-QLUIKEMJ.js +1197 -0
  47. package/dist/chunk-QLUIKEMJ.js.map +1 -0
  48. package/dist/chunk-ROJV6RON.js +3 -0
  49. package/dist/chunk-ROJV6RON.js.map +1 -0
  50. package/dist/chunk-S43IHHBU.js +82 -0
  51. package/dist/chunk-S43IHHBU.js.map +1 -0
  52. package/dist/chunk-TCAXRWT6.js +108 -0
  53. package/dist/chunk-TCAXRWT6.js.map +1 -0
  54. package/dist/chunk-TQB5LVOH.js +17 -0
  55. package/dist/chunk-TQB5LVOH.js.map +1 -0
  56. package/dist/chunk-VM2CXEUA.js +46 -0
  57. package/dist/chunk-VM2CXEUA.js.map +1 -0
  58. package/dist/chunk-W2JVHGFR.js +103 -0
  59. package/dist/chunk-W2JVHGFR.js.map +1 -0
  60. package/dist/chunk-WL67XCH5.js +59 -0
  61. package/dist/chunk-WL67XCH5.js.map +1 -0
  62. package/dist/chunk-XCNCIG2I.js +165 -0
  63. package/dist/chunk-XCNCIG2I.js.map +1 -0
  64. package/dist/chunk-XSLBANUV.js +16 -0
  65. package/dist/chunk-XSLBANUV.js.map +1 -0
  66. package/dist/chunk-ZY4NLITM.js +73 -0
  67. package/dist/chunk-ZY4NLITM.js.map +1 -0
  68. package/dist/data.cjs +59 -0
  69. package/dist/data.cjs.map +1 -0
  70. package/dist/data.d.cts +46 -0
  71. package/dist/data.d.ts +46 -0
  72. package/dist/data.js +4 -0
  73. package/dist/data.js.map +1 -0
  74. package/dist/error-boundary.cjs +115 -0
  75. package/dist/error-boundary.cjs.map +1 -0
  76. package/dist/error-boundary.d.cts +60 -0
  77. package/dist/error-boundary.d.ts +60 -0
  78. package/dist/error-boundary.js +4 -0
  79. package/dist/error-boundary.js.map +1 -0
  80. package/dist/floating.cjs +149 -0
  81. package/dist/floating.cjs.map +1 -0
  82. package/dist/floating.d.cts +106 -0
  83. package/dist/floating.d.ts +106 -0
  84. package/dist/floating.js +4 -0
  85. package/dist/floating.js.map +1 -0
  86. package/dist/form.cjs +144 -0
  87. package/dist/form.cjs.map +1 -0
  88. package/dist/form.d.cts +128 -0
  89. package/dist/form.d.ts +128 -0
  90. package/dist/form.js +3 -0
  91. package/dist/form.js.map +1 -0
  92. package/dist/i18n.cjs +112 -0
  93. package/dist/i18n.cjs.map +1 -0
  94. package/dist/i18n.d.cts +99 -0
  95. package/dist/i18n.d.ts +99 -0
  96. package/dist/i18n.js +3 -0
  97. package/dist/i18n.js.map +1 -0
  98. package/dist/index.cjs +18673 -0
  99. package/dist/index.cjs.map +1 -0
  100. package/dist/index.d.cts +8087 -0
  101. package/dist/index.d.ts +8087 -0
  102. package/dist/index.js +13525 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/layouts.cjs +392 -0
  105. package/dist/layouts.cjs.map +1 -0
  106. package/dist/layouts.d.cts +406 -0
  107. package/dist/layouts.d.ts +406 -0
  108. package/dist/layouts.js +5 -0
  109. package/dist/layouts.js.map +1 -0
  110. package/dist/machine.cjs +19 -0
  111. package/dist/machine.cjs.map +1 -0
  112. package/dist/machine.d.cts +18 -0
  113. package/dist/machine.d.ts +18 -0
  114. package/dist/machine.js +4 -0
  115. package/dist/machine.js.map +1 -0
  116. package/dist/modal-utils.cjs +170 -0
  117. package/dist/modal-utils.cjs.map +1 -0
  118. package/dist/modal-utils.d.cts +43 -0
  119. package/dist/modal-utils.d.ts +43 -0
  120. package/dist/modal-utils.js +3 -0
  121. package/dist/modal-utils.js.map +1 -0
  122. package/dist/motion.cjs +88 -0
  123. package/dist/motion.cjs.map +1 -0
  124. package/dist/motion.d.cts +43 -0
  125. package/dist/motion.d.ts +43 -0
  126. package/dist/motion.js +3 -0
  127. package/dist/motion.js.map +1 -0
  128. package/dist/provider.cjs +272 -0
  129. package/dist/provider.cjs.map +1 -0
  130. package/dist/provider.d.cts +130 -0
  131. package/dist/provider.d.ts +130 -0
  132. package/dist/provider.js +6 -0
  133. package/dist/provider.js.map +1 -0
  134. package/dist/resource.cjs +25 -0
  135. package/dist/resource.cjs.map +1 -0
  136. package/dist/resource.d.cts +17 -0
  137. package/dist/resource.d.ts +17 -0
  138. package/dist/resource.js +3 -0
  139. package/dist/resource.js.map +1 -0
  140. package/dist/skeletons.cjs +1659 -0
  141. package/dist/skeletons.cjs.map +1 -0
  142. package/dist/skeletons.d.cts +179 -0
  143. package/dist/skeletons.d.ts +179 -0
  144. package/dist/skeletons.js +7 -0
  145. package/dist/skeletons.js.map +1 -0
  146. package/dist/skins.cjs +138 -0
  147. package/dist/skins.cjs.map +1 -0
  148. package/dist/skins.d.cts +67 -0
  149. package/dist/skins.d.ts +67 -0
  150. package/dist/skins.js +3 -0
  151. package/dist/skins.js.map +1 -0
  152. package/dist/theme.cjs +106 -0
  153. package/dist/theme.cjs.map +1 -0
  154. package/dist/theme.d.cts +103 -0
  155. package/dist/theme.d.ts +103 -0
  156. package/dist/theme.js +3 -0
  157. package/dist/theme.js.map +1 -0
  158. package/dist/undo.cjs +51 -0
  159. package/dist/undo.cjs.map +1 -0
  160. package/dist/undo.d.cts +48 -0
  161. package/dist/undo.d.ts +48 -0
  162. package/dist/undo.js +3 -0
  163. package/dist/undo.js.map +1 -0
  164. package/package.json +59 -0
@@ -0,0 +1,1659 @@
1
+ 'use strict';
2
+
3
+ var vue = require('vue');
4
+ var core = require('@iris-ui-kit/core');
5
+
6
+ // src/skeletons/LoginTemplate.ts
7
+ var ALIGN_MAP = {
8
+ start: "flex-start",
9
+ center: "center",
10
+ end: "flex-end",
11
+ stretch: "stretch"
12
+ };
13
+ var JUSTIFY_MAP = {
14
+ start: "flex-start",
15
+ center: "center",
16
+ end: "flex-end",
17
+ between: "space-between",
18
+ around: "space-around"
19
+ };
20
+ function toCssSpacing(spacing) {
21
+ if (typeof spacing === "number") return `${spacing}px`;
22
+ if (spacing === "sm" || spacing === "md" || spacing === "lg") {
23
+ return `var(--iris-gap-${spacing})`;
24
+ }
25
+ return spacing;
26
+ }
27
+ var IrisStack = vue.defineComponent({
28
+ name: "IrisStack",
29
+ inheritAttrs: false,
30
+ props: {
31
+ direction: { type: String, default: "column" },
32
+ spacing: { type: [String, Number], default: "md" },
33
+ align: { type: String, default: "stretch" },
34
+ justify: { type: String, default: "start" },
35
+ wrap: { type: Boolean, default: false },
36
+ /** When true, renders as an inline-flex container. */
37
+ inline: { type: Boolean, default: false }
38
+ },
39
+ setup(props, { slots, attrs }) {
40
+ const style = vue.computed(() => ({
41
+ display: props.inline ? "inline-flex" : "flex",
42
+ flexDirection: props.direction,
43
+ gap: toCssSpacing(props.spacing),
44
+ alignItems: ALIGN_MAP[props.align],
45
+ justifyContent: JUSTIFY_MAP[props.justify],
46
+ flexWrap: props.wrap ? "wrap" : "nowrap"
47
+ }));
48
+ return () => vue.h(
49
+ "div",
50
+ {
51
+ ...attrs,
52
+ "data-iris-stack": "",
53
+ "data-iris-stack-direction": props.direction,
54
+ style: { ...style.value, ...attrs.style ?? {} }
55
+ },
56
+ slots.default?.()
57
+ );
58
+ }
59
+ });
60
+ var WIDTH_MAP = {
61
+ sm: "640px",
62
+ md: "768px",
63
+ lg: "1024px",
64
+ xl: "1280px",
65
+ full: "100%"
66
+ };
67
+ function resolveMaxWidth(input) {
68
+ if (input === "sm" || input === "md" || input === "lg" || input === "xl" || input === "full") {
69
+ return WIDTH_MAP[input];
70
+ }
71
+ return input;
72
+ }
73
+ function resolvePadding(input) {
74
+ if (typeof input === "number") return `${input}px`;
75
+ if (input === "sm" || input === "md" || input === "lg") {
76
+ return `var(--iris-padding-${input})`;
77
+ }
78
+ return input;
79
+ }
80
+ var IrisContainer = vue.defineComponent({
81
+ name: "IrisContainer",
82
+ inheritAttrs: false,
83
+ props: {
84
+ maxWidth: { type: String, default: "lg" },
85
+ padding: { type: [String, Number], default: "md" },
86
+ center: { type: Boolean, default: true }
87
+ },
88
+ setup(props, { slots, attrs }) {
89
+ const style = vue.computed(() => ({
90
+ width: "100%",
91
+ maxWidth: resolveMaxWidth(props.maxWidth),
92
+ padding: `0 ${resolvePadding(props.padding)}`,
93
+ ...props.center ? { marginInlineStart: "auto", marginInlineEnd: "auto" } : {}
94
+ }));
95
+ return () => vue.h(
96
+ "div",
97
+ {
98
+ ...attrs,
99
+ "data-iris-container": "",
100
+ "data-iris-container-max-width": props.maxWidth,
101
+ style: { ...style.value, ...attrs.style ?? {} }
102
+ },
103
+ slots.default?.()
104
+ );
105
+ }
106
+ });
107
+ var IrisInput = vue.defineComponent({
108
+ name: "IrisInput",
109
+ inheritAttrs: false,
110
+ props: {
111
+ modelValue: { type: [String, Number], default: "" },
112
+ type: { type: String, default: "text" },
113
+ size: { type: String, default: "md" },
114
+ placeholder: { type: String, default: "" },
115
+ disabled: { type: Boolean, default: false },
116
+ readonly: { type: Boolean, default: false },
117
+ /** Visual error state — does NOT enforce validation. */
118
+ invalid: { type: Boolean, default: false },
119
+ id: { type: String, default: void 0 },
120
+ /** Forwarded as `aria-describedby` on the inner <input>. Set by IrisFormField. */
121
+ ariaDescribedby: { type: String, default: void 0 }
122
+ },
123
+ emits: {
124
+ "update:modelValue": (_value) => true,
125
+ focus: (_event) => true,
126
+ blur: (_event) => true
127
+ },
128
+ setup(props, { slots, attrs, emit }) {
129
+ const focused = vue.ref(false);
130
+ const sizeStyles = vue.computed(() => {
131
+ const map = {
132
+ sm: {
133
+ padding: "4px var(--iris-padding-sm)",
134
+ fontSize: "12px",
135
+ minHeight: "28px"
136
+ },
137
+ md: {
138
+ padding: "6px var(--iris-padding-md)",
139
+ fontSize: "14px",
140
+ minHeight: "34px"
141
+ },
142
+ lg: {
143
+ padding: "8px var(--iris-padding-md)",
144
+ fontSize: "16px",
145
+ minHeight: "40px"
146
+ }
147
+ };
148
+ return map[props.size];
149
+ });
150
+ const wrapperStyle = vue.computed(() => {
151
+ const borderColor = props.invalid ? "var(--iris-danger)" : focused.value ? "var(--iris-primary)" : "var(--iris-border)";
152
+ const boxShadow = focused.value ? `0 0 0 3px ${props.invalid ? "rgba(239, 68, 68, 0.18)" : "rgba(99, 102, 241, 0.18)"}` : "none";
153
+ return {
154
+ display: "inline-flex",
155
+ alignItems: "center",
156
+ gap: "var(--iris-gap-sm)",
157
+ background: "var(--iris-background)",
158
+ color: "var(--iris-foreground)",
159
+ border: `1px solid ${borderColor}`,
160
+ borderRadius: "var(--iris-radius-md)",
161
+ cursor: props.disabled ? "not-allowed" : "text",
162
+ opacity: props.disabled ? "0.6" : "1",
163
+ transition: "border-color 120ms ease, box-shadow 120ms ease",
164
+ boxShadow,
165
+ ...sizeStyles.value,
166
+ padding: sizeStyles.value.padding,
167
+ minHeight: sizeStyles.value.minHeight
168
+ };
169
+ });
170
+ const inputStyle = {
171
+ flex: "1",
172
+ minWidth: "0",
173
+ border: "none",
174
+ outline: "none",
175
+ background: "transparent",
176
+ color: "inherit",
177
+ fontFamily: "inherit",
178
+ fontSize: "inherit",
179
+ padding: "0"
180
+ };
181
+ const onInput = (event) => {
182
+ emit("update:modelValue", event.target.value);
183
+ };
184
+ const onFocus = (event) => {
185
+ focused.value = true;
186
+ emit("focus", event);
187
+ };
188
+ const onBlur = (event) => {
189
+ focused.value = false;
190
+ emit("blur", event);
191
+ };
192
+ return () => {
193
+ const prefix = slots.prefix?.();
194
+ const suffix = slots.suffix?.();
195
+ return vue.h(
196
+ "label",
197
+ {
198
+ ...attrs,
199
+ "data-iris-input": "",
200
+ "data-iris-input-size": props.size,
201
+ "data-state": props.invalid ? "invalid" : focused.value ? "focused" : "idle",
202
+ style: {
203
+ ...wrapperStyle.value,
204
+ ...attrs.style ?? {}
205
+ }
206
+ },
207
+ [
208
+ prefix && vue.h(
209
+ "span",
210
+ {
211
+ "data-iris-input-prefix": "",
212
+ style: { display: "inline-flex", alignItems: "center", color: "var(--iris-muted)" }
213
+ },
214
+ prefix
215
+ ),
216
+ vue.h("input", {
217
+ id: props.id,
218
+ type: props.type,
219
+ value: props.modelValue,
220
+ placeholder: props.placeholder,
221
+ disabled: props.disabled || void 0,
222
+ readonly: props.readonly || void 0,
223
+ "aria-invalid": props.invalid ? "true" : void 0,
224
+ "aria-describedby": props.ariaDescribedby,
225
+ style: inputStyle,
226
+ onInput,
227
+ onFocus,
228
+ onBlur
229
+ }),
230
+ suffix && vue.h(
231
+ "span",
232
+ {
233
+ "data-iris-input-suffix": "",
234
+ style: { display: "inline-flex", alignItems: "center", color: "var(--iris-muted)" }
235
+ },
236
+ suffix
237
+ )
238
+ ]
239
+ );
240
+ };
241
+ }
242
+ });
243
+
244
+ // src/i18n/context.ts
245
+ var I18nInjectionKey = /* @__PURE__ */ Symbol("IrisI18n");
246
+ var fallbackI18n = null;
247
+ function getFallback() {
248
+ if (fallbackI18n === null) fallbackI18n = core.createI18n();
249
+ return fallbackI18n;
250
+ }
251
+ function useI18n() {
252
+ const i18n = vue.inject(I18nInjectionKey, null) ?? getFallback();
253
+ const state = vue.ref(i18n.getState());
254
+ const unsubscribe = i18n.subscribe((next) => {
255
+ state.value = next;
256
+ });
257
+ vue.onBeforeUnmount(unsubscribe);
258
+ return {
259
+ locale: vue.computed(() => state.value.locale),
260
+ t: (key, params) => {
261
+ void state.value.locale;
262
+ return i18n.t(key, params);
263
+ },
264
+ formatDate: (value, options) => {
265
+ void state.value.locale;
266
+ return i18n.formatDate(value, options);
267
+ },
268
+ formatNumber: (value, options) => {
269
+ void state.value.locale;
270
+ return i18n.formatNumber(value, options);
271
+ },
272
+ formatRelativeTime: (value, unit, options) => {
273
+ void state.value.locale;
274
+ return i18n.formatRelativeTime(value, unit, options);
275
+ },
276
+ setLocale: i18n.setLocale,
277
+ setMessages: i18n.setMessages
278
+ };
279
+ }
280
+
281
+ // src/primitives/password-input/PasswordInput.ts
282
+ var IrisPasswordInput = vue.defineComponent({
283
+ name: "IrisPasswordInput",
284
+ inheritAttrs: false,
285
+ props: {
286
+ modelValue: { type: String, default: "" },
287
+ size: { type: String, default: "md" },
288
+ placeholder: { type: String, default: "" },
289
+ disabled: { type: Boolean, default: false },
290
+ readonly: { type: Boolean, default: false },
291
+ invalid: { type: Boolean, default: false },
292
+ id: { type: String, default: void 0 },
293
+ /** Allow the user to toggle visibility. Default `true`. */
294
+ showToggle: { type: Boolean, default: true }
295
+ },
296
+ emits: {
297
+ "update:modelValue": (_value) => true,
298
+ focus: (_event) => true,
299
+ blur: (_event) => true
300
+ },
301
+ setup(props, { attrs, emit, slots }) {
302
+ const { t } = useI18n();
303
+ const visible = vue.ref(false);
304
+ const onUpdate = (value) => emit("update:modelValue", value);
305
+ const onFocus = (e) => emit("focus", e);
306
+ const onBlur = (e) => emit("blur", e);
307
+ const toggle = () => {
308
+ if (props.disabled || props.readonly) return;
309
+ visible.value = !visible.value;
310
+ };
311
+ return () => vue.h(
312
+ IrisInput,
313
+ {
314
+ ...attrs,
315
+ modelValue: props.modelValue,
316
+ type: visible.value ? "text" : "password",
317
+ size: props.size,
318
+ placeholder: props.placeholder,
319
+ disabled: props.disabled,
320
+ readonly: props.readonly,
321
+ invalid: props.invalid,
322
+ id: props.id,
323
+ "data-iris-password-input": "",
324
+ "onUpdate:modelValue": onUpdate,
325
+ onFocus,
326
+ onBlur
327
+ },
328
+ {
329
+ prefix: slots.prefix,
330
+ suffix: () => [
331
+ slots.suffix?.(),
332
+ props.showToggle ? vue.h(
333
+ "button",
334
+ {
335
+ type: "button",
336
+ "data-iris-password-input-toggle": "",
337
+ "aria-label": visible.value ? t("passwordInput.hide") : t("passwordInput.show"),
338
+ "aria-pressed": visible.value ? "true" : "false",
339
+ onClick: toggle,
340
+ style: {
341
+ background: "transparent",
342
+ border: "none",
343
+ cursor: props.disabled ? "not-allowed" : "pointer",
344
+ color: "var(--iris-muted)",
345
+ padding: "0 2px",
346
+ fontSize: "13px",
347
+ lineHeight: "1"
348
+ }
349
+ },
350
+ visible.value ? "\u{1F648}" : "\u{1F441}"
351
+ ) : null
352
+ ]
353
+ }
354
+ );
355
+ }
356
+ });
357
+ var IrisCheckbox = vue.defineComponent({
358
+ name: "IrisCheckbox",
359
+ inheritAttrs: false,
360
+ props: {
361
+ modelValue: {
362
+ type: [Boolean, String],
363
+ default: false
364
+ },
365
+ size: { type: String, default: "md" },
366
+ disabled: { type: Boolean, default: false },
367
+ id: { type: String, default: void 0 },
368
+ name: { type: String, default: void 0 },
369
+ value: { type: [String, Number], default: void 0 },
370
+ /** Forwarded as `aria-describedby` on the native checkbox input. Set by IrisFormField. */
371
+ ariaDescribedby: { type: String, default: void 0 },
372
+ /**
373
+ * Accessible name for the native checkbox input. Bound as `aria-label` on
374
+ * the `<input>` itself (the element with the checkbox role), NOT the label
375
+ * wrapper. Use when there is no visible label text.
376
+ */
377
+ ariaLabel: { type: String, default: void 0 },
378
+ /** Invalid state — sets aria-invalid. Set by IrisFormField. */
379
+ invalid: { type: Boolean, default: false }
380
+ },
381
+ emits: {
382
+ "update:modelValue": (_value) => true,
383
+ change: (_value) => true
384
+ },
385
+ setup(props, { slots, attrs, emit }) {
386
+ const state = vue.computed(() => {
387
+ if (props.modelValue === "indeterminate") return "indeterminate";
388
+ return props.modelValue ? "checked" : "unchecked";
389
+ });
390
+ const dim = vue.computed(() => {
391
+ const map = { sm: "14px", md: "18px", lg: "22px" };
392
+ return map[props.size];
393
+ });
394
+ const boxStyle = vue.computed(() => ({
395
+ display: "inline-flex",
396
+ alignItems: "center",
397
+ justifyContent: "center",
398
+ width: dim.value,
399
+ height: dim.value,
400
+ borderRadius: "var(--iris-radius-sm)",
401
+ border: "1px solid var(--iris-border)",
402
+ background: state.value === "unchecked" ? "var(--iris-background)" : "var(--iris-primary)",
403
+ borderColor: state.value === "unchecked" ? "var(--iris-border)" : "var(--iris-primary)",
404
+ color: "var(--iris-primary-foreground)",
405
+ cursor: props.disabled ? "not-allowed" : "pointer",
406
+ opacity: props.disabled ? "0.6" : "1",
407
+ transition: "background-color 120ms ease, border-color 120ms ease",
408
+ verticalAlign: "middle"
409
+ }));
410
+ const onChange = (event) => {
411
+ const next = event.target.checked;
412
+ emit("update:modelValue", next);
413
+ emit("change", next);
414
+ };
415
+ const renderIndicator = () => {
416
+ if (state.value === "unchecked") return null;
417
+ if (state.value === "indeterminate") {
418
+ return vue.h("span", {
419
+ "aria-hidden": "true",
420
+ style: { width: "60%", height: "2px", background: "currentColor", borderRadius: "1px" }
421
+ });
422
+ }
423
+ return vue.h(
424
+ "svg",
425
+ {
426
+ "aria-hidden": "true",
427
+ viewBox: "0 0 16 16",
428
+ width: "80%",
429
+ height: "80%",
430
+ fill: "none"
431
+ },
432
+ [
433
+ vue.h("path", {
434
+ d: "M3 8.5 L6.5 12 L13 4.5",
435
+ stroke: "currentColor",
436
+ "stroke-width": "2.4",
437
+ "stroke-linecap": "round",
438
+ "stroke-linejoin": "round"
439
+ })
440
+ ]
441
+ );
442
+ };
443
+ return () => vue.h(
444
+ "label",
445
+ {
446
+ ...attrs,
447
+ "data-iris-checkbox": "",
448
+ "data-iris-checkbox-size": props.size,
449
+ "data-state": state.value,
450
+ style: {
451
+ display: "inline-flex",
452
+ alignItems: "center",
453
+ gap: "var(--iris-gap-sm)",
454
+ cursor: props.disabled ? "not-allowed" : "pointer",
455
+ userSelect: "none",
456
+ ...attrs.style ?? {}
457
+ }
458
+ },
459
+ [
460
+ vue.h("input", {
461
+ type: "checkbox",
462
+ id: props.id,
463
+ name: props.name,
464
+ value: props.value,
465
+ checked: props.modelValue === true,
466
+ indeterminate: props.modelValue === "indeterminate",
467
+ disabled: props.disabled || void 0,
468
+ "aria-checked": state.value === "indeterminate" ? "mixed" : state.value === "checked" ? "true" : "false",
469
+ "aria-describedby": props.ariaDescribedby,
470
+ "aria-label": props.ariaLabel,
471
+ "aria-invalid": props.invalid ? "true" : void 0,
472
+ style: {
473
+ position: "absolute",
474
+ opacity: "0",
475
+ width: "0",
476
+ height: "0",
477
+ pointerEvents: "none"
478
+ },
479
+ onChange
480
+ }),
481
+ vue.h("span", { "aria-hidden": "true", style: boxStyle.value }, [renderIndicator()]),
482
+ slots.default && vue.h("span", null, slots.default())
483
+ ]
484
+ );
485
+ }
486
+ });
487
+
488
+ // src/primitives/button/styles.ts
489
+ var STYLE_ID = "iris-button-styles";
490
+ var CSS = `
491
+ .iris-button {
492
+ cursor: pointer;
493
+ display: inline-flex;
494
+ align-items: center;
495
+ justify-content: center;
496
+ gap: var(--iris-gap-sm, 4px);
497
+ font-family: inherit;
498
+ font-weight: 500;
499
+ line-height: 1.2;
500
+ border-radius: var(--iris-radius-md, 6px);
501
+ white-space: nowrap;
502
+ user-select: none;
503
+ position: relative;
504
+ transition:
505
+ background-color 120ms ease,
506
+ border-color 120ms ease,
507
+ color 120ms ease,
508
+ filter 120ms ease;
509
+ }
510
+ .iris-button:focus-visible {
511
+ outline: 2px solid var(--iris-primary);
512
+ outline-offset: 2px;
513
+ }
514
+ .iris-button[disabled],
515
+ .iris-button[aria-busy="true"] {
516
+ cursor: not-allowed;
517
+ opacity: 0.6;
518
+ }
519
+ .iris-button[data-iris-button-variant="solid"]:not([disabled]):not([aria-busy="true"]):hover {
520
+ filter: brightness(1.08);
521
+ }
522
+ .iris-button[data-iris-button-variant="outline"]:not([disabled]):not([aria-busy="true"]):hover,
523
+ .iris-button[data-iris-button-variant="ghost"]:not([disabled]):not([aria-busy="true"]):hover {
524
+ background-color: var(--iris-surface-hover);
525
+ }
526
+ .iris-button[data-iris-button-variant="link"]:not([disabled]):not([aria-busy="true"]):hover {
527
+ text-decoration: underline;
528
+ }
529
+ .iris-button-leading {
530
+ display: inline-flex;
531
+ align-items: center;
532
+ }
533
+ .iris-button-spinner {
534
+ width: 1em;
535
+ height: 1em;
536
+ display: inline-block;
537
+ animation: iris-button-spin 0.7s linear infinite;
538
+ }
539
+ @keyframes iris-button-spin {
540
+ to { transform: rotate(360deg); }
541
+ }
542
+ `;
543
+ var installed = false;
544
+ function installButtonStyles() {
545
+ if (installed) return;
546
+ if (typeof document === "undefined") return;
547
+ if (document.getElementById(STYLE_ID)) {
548
+ installed = true;
549
+ return;
550
+ }
551
+ const style = document.createElement("style");
552
+ style.id = STYLE_ID;
553
+ style.textContent = CSS;
554
+ document.head.appendChild(style);
555
+ installed = true;
556
+ }
557
+ function isEventListenerKey(key) {
558
+ return key.startsWith("on") && key.length > 2 && /[A-Z]/.test(key.charAt(2));
559
+ }
560
+ function composeRefs(...refs) {
561
+ return (el) => {
562
+ for (const r of refs) {
563
+ if (r == null) continue;
564
+ if (typeof r === "function") {
565
+ r(el);
566
+ } else if (typeof r === "object" && "value" in r) {
567
+ r.value = el;
568
+ }
569
+ }
570
+ };
571
+ }
572
+ function mergeSlotProps(parent, child) {
573
+ const out = { ...child };
574
+ for (const key in parent) {
575
+ const parentVal = parent[key];
576
+ const childVal = child[key];
577
+ if (isEventListenerKey(key)) {
578
+ if (typeof parentVal === "function" && typeof childVal === "function") {
579
+ out[key] = core.composeEventHandlers(
580
+ parentVal,
581
+ childVal
582
+ );
583
+ } else if (typeof parentVal === "function" && childVal == null) {
584
+ out[key] = parentVal;
585
+ }
586
+ } else if (key === "ref") {
587
+ if (parentVal != null && childVal != null) {
588
+ out[key] = composeRefs(parentVal, childVal);
589
+ } else if (parentVal != null) {
590
+ out[key] = parentVal;
591
+ }
592
+ } else if (key === "class") {
593
+ const merged = [parentVal, childVal].filter(Boolean).join(" ").trim();
594
+ if (merged) out[key] = merged;
595
+ } else if (key === "style") {
596
+ if (parentVal || childVal) {
597
+ out[key] = { ...parentVal, ...childVal };
598
+ }
599
+ } else if (!(key in child)) {
600
+ out[key] = parentVal;
601
+ }
602
+ }
603
+ return out;
604
+ }
605
+ vue.defineComponent({
606
+ name: "IrisSlot",
607
+ inheritAttrs: false,
608
+ setup(_, { slots, attrs }) {
609
+ return () => {
610
+ const children = slots.default?.();
611
+ const root = findFirstElement(children);
612
+ if (!root) {
613
+ if (typeof process === "undefined" || process.env?.NODE_ENV !== "production") {
614
+ console.warn("[iris-ui] IrisSlot expected exactly one child element; got none.");
615
+ }
616
+ return null;
617
+ }
618
+ const merged = mergeSlotProps(
619
+ attrs,
620
+ root.props ?? {}
621
+ );
622
+ return vue.h(root.type, merged, root.children);
623
+ };
624
+ }
625
+ });
626
+ function findFirstElement(children) {
627
+ if (!children || children.length === 0) return null;
628
+ let found = null;
629
+ let extraCount = 0;
630
+ const walk = (nodes) => {
631
+ for (const node of nodes) {
632
+ if (node.type === vue.Fragment) {
633
+ const fragChildren = Array.isArray(node.children) ? node.children : [];
634
+ walk(fragChildren);
635
+ continue;
636
+ }
637
+ if (typeof node.type === "string" || typeof node.type === "object" || typeof node.type === "function") {
638
+ if (found) {
639
+ extraCount += 1;
640
+ } else {
641
+ found = node;
642
+ }
643
+ }
644
+ }
645
+ };
646
+ walk(children);
647
+ if (extraCount > 0 && (typeof process === "undefined" || process.env?.NODE_ENV !== "production")) {
648
+ console.warn(`[iris-ui] IrisSlot got ${extraCount + 1} root elements; only the first is used.`);
649
+ }
650
+ return found;
651
+ }
652
+
653
+ // src/primitives/button/Button.ts
654
+ var SIZE_STYLES = {
655
+ sm: {
656
+ padding: "var(--iris-padding-sm) var(--iris-padding-md)",
657
+ fontSize: "12px"
658
+ },
659
+ md: {
660
+ padding: "var(--iris-padding-sm) var(--iris-padding-lg)",
661
+ fontSize: "14px"
662
+ },
663
+ lg: {
664
+ padding: "var(--iris-padding-md) var(--iris-padding-lg)",
665
+ fontSize: "16px"
666
+ }
667
+ };
668
+ var VARIANT_STYLES = {
669
+ solid: {
670
+ background: "var(--iris-primary)",
671
+ color: "var(--iris-primary-foreground)",
672
+ border: "1px solid var(--iris-primary)"
673
+ },
674
+ outline: {
675
+ background: "transparent",
676
+ color: "var(--iris-primary)",
677
+ border: "1px solid var(--iris-border)"
678
+ },
679
+ ghost: {
680
+ background: "transparent",
681
+ color: "var(--iris-foreground)",
682
+ border: "1px solid transparent"
683
+ },
684
+ link: {
685
+ background: "transparent",
686
+ color: "var(--iris-primary)",
687
+ border: "1px solid transparent",
688
+ textDecoration: "none"
689
+ }
690
+ };
691
+ function buildInlineStyle(variant, size) {
692
+ const base = { ...SIZE_STYLES[size], ...VARIANT_STYLES[variant] };
693
+ if (variant === "link") {
694
+ base.padding = "0";
695
+ }
696
+ return base;
697
+ }
698
+ function renderSpinner() {
699
+ return vue.h(
700
+ "svg",
701
+ {
702
+ class: "iris-button-spinner",
703
+ viewBox: "0 0 24 24",
704
+ fill: "none",
705
+ "aria-hidden": "true"
706
+ },
707
+ [
708
+ vue.h("circle", {
709
+ cx: "12",
710
+ cy: "12",
711
+ r: "10",
712
+ stroke: "currentColor",
713
+ "stroke-opacity": "0.25",
714
+ "stroke-width": "3"
715
+ }),
716
+ vue.h("path", {
717
+ d: "M22 12a10 10 0 0 1-10 10",
718
+ stroke: "currentColor",
719
+ "stroke-width": "3",
720
+ "stroke-linecap": "round"
721
+ })
722
+ ]
723
+ );
724
+ }
725
+ var IrisButton = vue.defineComponent({
726
+ name: "IrisButton",
727
+ props: {
728
+ variant: { type: String, default: "solid" },
729
+ size: { type: String, default: "md" },
730
+ disabled: { type: Boolean, default: false },
731
+ loading: { type: Boolean, default: false },
732
+ type: { type: String, default: "button" },
733
+ asChild: { type: Boolean, default: false }
734
+ },
735
+ emits: {
736
+ click: (_event) => true
737
+ },
738
+ setup(props, { slots, emit }) {
739
+ installButtonStyles();
740
+ const isInteractive = vue.computed(() => !props.disabled && !props.loading);
741
+ const inlineStyle = vue.computed(() => buildInlineStyle(props.variant, props.size));
742
+ const onClick = (event) => {
743
+ if (!isInteractive.value) {
744
+ event.preventDefault();
745
+ event.stopPropagation();
746
+ return;
747
+ }
748
+ emit("click", event);
749
+ };
750
+ return () => {
751
+ const baseProps = {
752
+ class: "iris-button",
753
+ "data-iris-button-variant": props.variant,
754
+ "data-iris-button-size": props.size,
755
+ "aria-disabled": props.disabled ? "true" : void 0,
756
+ "aria-busy": props.loading ? "true" : void 0,
757
+ style: inlineStyle.value,
758
+ onClick
759
+ };
760
+ if (props.asChild) {
761
+ const root = findFirstElement(slots.default?.());
762
+ if (!root) {
763
+ if (typeof process === "undefined" || process.env?.NODE_ENV !== "production") {
764
+ console.warn("[iris-ui] IrisButton: as-child requires a single child element");
765
+ }
766
+ return null;
767
+ }
768
+ const parentProps = {
769
+ ...baseProps,
770
+ disabled: isInteractive.value ? void 0 : true
771
+ };
772
+ const merged = mergeSlotProps(parentProps, root.props ?? {});
773
+ return vue.h(root.type, merged, root.children);
774
+ }
775
+ const leadingContent = props.loading ? renderSpinner() : slots.leading?.();
776
+ const children = [];
777
+ if (leadingContent) {
778
+ children.push(vue.h("span", { class: "iris-button-leading" }, leadingContent));
779
+ }
780
+ const defaultSlot = slots.default?.();
781
+ if (defaultSlot) children.push(defaultSlot);
782
+ return vue.h(
783
+ "button",
784
+ {
785
+ ...baseProps,
786
+ type: props.type,
787
+ disabled: isInteractive.value ? void 0 : true
788
+ },
789
+ children
790
+ );
791
+ };
792
+ }
793
+ });
794
+ var IrisFormField = vue.defineComponent({
795
+ name: "IrisFormField",
796
+ inheritAttrs: false,
797
+ props: {
798
+ label: { type: String, default: "" },
799
+ hint: { type: String, default: "" },
800
+ error: { type: String, default: "" },
801
+ required: { type: Boolean, default: false },
802
+ /** Override the auto-generated control id. */
803
+ labelFor: { type: String, default: "" },
804
+ /** Visual size of the label. */
805
+ size: { type: String, default: "md" }
806
+ },
807
+ setup(props, { slots, attrs }) {
808
+ const baseId = vue.useId();
809
+ const controlId = vue.computed(() => props.labelFor || `${baseId}-control`);
810
+ const hintId = `${baseId}-hint`;
811
+ const errorId = `${baseId}-error`;
812
+ const describedBy = vue.computed(() => {
813
+ const ids = [];
814
+ if (props.hint) ids.push(hintId);
815
+ if (props.error) ids.push(errorId);
816
+ return ids.length > 0 ? ids.join(" ") : void 0;
817
+ });
818
+ const labelStyle = vue.computed(() => ({
819
+ fontSize: props.size === "sm" ? "12px" : "14px",
820
+ fontWeight: "500",
821
+ color: props.error ? "var(--iris-danger)" : "var(--iris-foreground)",
822
+ display: "inline-flex",
823
+ alignItems: "center",
824
+ gap: "4px"
825
+ }));
826
+ return () => {
827
+ const children = slots.default?.() ?? [];
828
+ let injected = null;
829
+ const restChildren = [];
830
+ let foundFirst = false;
831
+ children.forEach((child) => {
832
+ if (!foundFirst && typeof child.type !== "symbol" && typeof child.type !== "string") {
833
+ injected = vue.cloneVNode(child, {
834
+ id: controlId.value,
835
+ invalid: props.error ? true : void 0,
836
+ ariaDescribedby: describedBy.value
837
+ });
838
+ foundFirst = true;
839
+ } else {
840
+ restChildren.push(child);
841
+ }
842
+ });
843
+ return vue.h(
844
+ "div",
845
+ {
846
+ ...attrs,
847
+ "data-iris-form-field": "",
848
+ "data-iris-form-field-state": props.error ? "invalid" : "valid",
849
+ style: {
850
+ display: "flex",
851
+ flexDirection: "column",
852
+ gap: "4px",
853
+ ...attrs.style ?? {}
854
+ }
855
+ },
856
+ [
857
+ props.label ? vue.h(
858
+ "label",
859
+ {
860
+ for: controlId.value,
861
+ "data-iris-form-field-label": "",
862
+ style: labelStyle.value
863
+ },
864
+ [
865
+ props.label,
866
+ props.required ? vue.h(
867
+ "span",
868
+ {
869
+ "aria-hidden": "true",
870
+ "data-iris-form-field-required": "",
871
+ style: { color: "var(--iris-danger)" }
872
+ },
873
+ "*"
874
+ ) : null
875
+ ]
876
+ ) : null,
877
+ // The wrapped control + any trailing siblings.
878
+ vue.h(vue.Fragment, null, [injected, ...restChildren]),
879
+ props.hint && !props.error ? vue.h(
880
+ "div",
881
+ {
882
+ id: hintId,
883
+ "data-iris-form-field-hint": "",
884
+ style: { fontSize: "12px", color: "var(--iris-muted)" }
885
+ },
886
+ props.hint
887
+ ) : null,
888
+ props.error ? vue.h(
889
+ "div",
890
+ {
891
+ id: errorId,
892
+ "data-iris-form-field-error": "",
893
+ role: "alert",
894
+ style: { fontSize: "12px", color: "var(--iris-danger)" }
895
+ },
896
+ props.error
897
+ ) : null
898
+ ]
899
+ );
900
+ };
901
+ }
902
+ });
903
+ var TONE_TO_VAR = {
904
+ info: "--iris-primary",
905
+ success: "--iris-success",
906
+ warning: "--iris-warning",
907
+ danger: "--iris-danger"
908
+ };
909
+ var IrisAlert = vue.defineComponent({
910
+ name: "IrisAlert",
911
+ inheritAttrs: false,
912
+ props: {
913
+ tone: { type: String, default: "info" },
914
+ title: { type: String, default: "" },
915
+ /** When true, a close button is rendered. */
916
+ closable: { type: Boolean, default: false },
917
+ /** Controlled visibility. Omit for uncontrolled (defaults to visible). */
918
+ open: { type: Boolean, default: void 0 }
919
+ },
920
+ emits: {
921
+ "update:open": (_value) => true,
922
+ close: () => true
923
+ },
924
+ setup(props, { slots, attrs, emit }) {
925
+ const { t } = useI18n();
926
+ const internalOpen = vue.ref(true);
927
+ vue.watch(
928
+ () => props.open,
929
+ (value) => {
930
+ if (value !== void 0) internalOpen.value = value;
931
+ }
932
+ );
933
+ const isOpen = vue.computed(() => props.open !== void 0 ? props.open : internalOpen.value);
934
+ const onClose = () => {
935
+ if (props.open === void 0) internalOpen.value = false;
936
+ emit("update:open", false);
937
+ emit("close");
938
+ };
939
+ const tonalVar = vue.computed(() => `var(${TONE_TO_VAR[props.tone]})`);
940
+ const containerStyle = vue.computed(() => ({
941
+ display: "flex",
942
+ gap: "var(--iris-gap-md, 12px)",
943
+ padding: "var(--iris-padding-md, 12px)",
944
+ borderRadius: "var(--iris-radius-md, 6px)",
945
+ border: `1px solid ${tonalVar.value}`,
946
+ // `backgroundColor` is the precomputed fallback under color-mix (engines
947
+ // without it); the `background` shorthand overrides with the exact mix.
948
+ backgroundColor: `var(${TONE_TO_VAR[props.tone]}-subtle)`,
949
+ background: `color-mix(in srgb, ${tonalVar.value} 10%, var(--iris-background))`,
950
+ color: "var(--iris-foreground)",
951
+ alignItems: "flex-start"
952
+ }));
953
+ return () => {
954
+ if (!isOpen.value) return null;
955
+ return vue.h(
956
+ "div",
957
+ {
958
+ ...attrs,
959
+ role: props.tone === "danger" || props.tone === "warning" ? "alert" : "status",
960
+ "data-iris-alert": "",
961
+ "data-iris-alert-tone": props.tone,
962
+ style: { ...containerStyle.value, ...attrs.style ?? {} }
963
+ },
964
+ [
965
+ slots.icon ? vue.h(
966
+ "span",
967
+ {
968
+ "data-iris-alert-icon": "",
969
+ style: { color: tonalVar.value, flexShrink: "0", display: "inline-flex" }
970
+ },
971
+ slots.icon()
972
+ ) : null,
973
+ vue.h("div", { "data-iris-alert-body": "", style: { flex: "1", minWidth: "0" } }, [
974
+ props.title || slots.title ? vue.h(
975
+ "div",
976
+ {
977
+ "data-iris-alert-title": "",
978
+ style: { fontWeight: "600", marginBottom: "4px", color: tonalVar.value }
979
+ },
980
+ slots.title?.() ?? props.title
981
+ ) : null,
982
+ vue.h("div", { "data-iris-alert-content": "" }, slots.default?.())
983
+ ]),
984
+ props.closable ? vue.h(
985
+ "button",
986
+ {
987
+ type: "button",
988
+ "data-iris-alert-close": "",
989
+ "aria-label": t("alert.close"),
990
+ onClick: onClose,
991
+ style: {
992
+ background: "transparent",
993
+ border: "none",
994
+ cursor: "pointer",
995
+ color: "var(--iris-muted)",
996
+ fontSize: "16px",
997
+ padding: "0",
998
+ lineHeight: "1",
999
+ flexShrink: "0"
1000
+ }
1001
+ },
1002
+ "\u2715"
1003
+ ) : null
1004
+ ]
1005
+ );
1006
+ };
1007
+ }
1008
+ });
1009
+ var SPACING_MAP = {
1010
+ sm: "8px",
1011
+ md: "16px",
1012
+ lg: "24px"
1013
+ };
1014
+ var IrisDivider = vue.defineComponent({
1015
+ name: "IrisDivider",
1016
+ inheritAttrs: false,
1017
+ props: {
1018
+ orientation: { type: String, default: "horizontal" },
1019
+ label: { type: String, default: "" },
1020
+ spacing: { type: String, default: "md" }
1021
+ },
1022
+ setup(props, { slots, attrs }) {
1023
+ const isHorizontal = vue.computed(() => props.orientation === "horizontal");
1024
+ const hasLabel = vue.computed(() => Boolean(props.label || slots.default));
1025
+ return () => {
1026
+ if (isHorizontal.value && !hasLabel.value) {
1027
+ return vue.h("hr", {
1028
+ ...attrs,
1029
+ "data-iris-divider": "",
1030
+ "data-iris-divider-orientation": "horizontal",
1031
+ style: {
1032
+ border: "none",
1033
+ borderTop: "1px solid var(--iris-border)",
1034
+ margin: `${SPACING_MAP[props.spacing]} 0`,
1035
+ width: "100%",
1036
+ ...attrs.style ?? {}
1037
+ }
1038
+ });
1039
+ }
1040
+ if (!isHorizontal.value) {
1041
+ return vue.h("div", {
1042
+ ...attrs,
1043
+ role: "separator",
1044
+ "aria-orientation": "vertical",
1045
+ "data-iris-divider": "",
1046
+ "data-iris-divider-orientation": "vertical",
1047
+ style: {
1048
+ display: "inline-block",
1049
+ width: "1px",
1050
+ alignSelf: "stretch",
1051
+ background: "var(--iris-border)",
1052
+ margin: `0 ${SPACING_MAP[props.spacing]}`,
1053
+ ...attrs.style ?? {}
1054
+ }
1055
+ });
1056
+ }
1057
+ return vue.h(
1058
+ "div",
1059
+ {
1060
+ ...attrs,
1061
+ role: "separator",
1062
+ "aria-orientation": "horizontal",
1063
+ "data-iris-divider": "",
1064
+ "data-iris-divider-orientation": "horizontal",
1065
+ "data-iris-divider-has-label": "true",
1066
+ style: {
1067
+ display: "flex",
1068
+ alignItems: "center",
1069
+ gap: "8px",
1070
+ margin: `${SPACING_MAP[props.spacing]} 0`,
1071
+ color: "var(--iris-muted)",
1072
+ fontSize: "12px",
1073
+ textTransform: "uppercase",
1074
+ letterSpacing: "0.04em",
1075
+ ...attrs.style ?? {}
1076
+ }
1077
+ },
1078
+ [
1079
+ vue.h("span", { "data-iris-divider-line": "before", style: lineStyle() }),
1080
+ vue.h("span", { "data-iris-divider-label": "" }, slots.default?.() ?? props.label),
1081
+ vue.h("span", { "data-iris-divider-line": "after", style: lineStyle() })
1082
+ ]
1083
+ );
1084
+ };
1085
+ }
1086
+ });
1087
+ function lineStyle() {
1088
+ return {
1089
+ flex: "1",
1090
+ height: "1px",
1091
+ background: "var(--iris-border)"
1092
+ };
1093
+ }
1094
+
1095
+ // src/skeletons/LoginTemplate.ts
1096
+ var IrisLoginTemplate = vue.defineComponent({
1097
+ name: "IrisLoginTemplate",
1098
+ inheritAttrs: false,
1099
+ props: {
1100
+ title: { type: String, default: "Sign in" },
1101
+ description: { type: String, default: "" },
1102
+ /** Show "Remember me" checkbox. */
1103
+ showRemember: { type: Boolean, default: true },
1104
+ /** Error message shown above the form. */
1105
+ error: { type: String, default: "" },
1106
+ submitLabel: { type: String, default: "Sign in" },
1107
+ /** Disable the entire form (e.g. while a request is pending). */
1108
+ loading: { type: Boolean, default: false }
1109
+ },
1110
+ emits: {
1111
+ submit: (_payload) => true
1112
+ },
1113
+ setup(props, { attrs, slots, emit }) {
1114
+ const email = vue.ref("");
1115
+ const password = vue.ref("");
1116
+ const remember = vue.ref(false);
1117
+ const onSubmit = (event) => {
1118
+ event.preventDefault();
1119
+ emit("submit", {
1120
+ email: email.value,
1121
+ password: password.value,
1122
+ remember: remember.value
1123
+ });
1124
+ };
1125
+ return () => vue.h(
1126
+ "div",
1127
+ {
1128
+ ...attrs,
1129
+ "data-iris-login-template": "",
1130
+ style: {
1131
+ minHeight: "100vh",
1132
+ display: "flex",
1133
+ alignItems: "center",
1134
+ justifyContent: "center",
1135
+ background: "var(--iris-background)",
1136
+ color: "var(--iris-foreground)",
1137
+ padding: "24px",
1138
+ ...attrs.style ?? {}
1139
+ }
1140
+ },
1141
+ vue.h(IrisContainer, { maxWidth: "420px", padding: 0 }, () => [
1142
+ vue.h(
1143
+ "form",
1144
+ {
1145
+ onSubmit,
1146
+ "data-iris-login-form": "",
1147
+ style: {
1148
+ background: "var(--iris-surface)",
1149
+ border: "1px solid var(--iris-border)",
1150
+ borderRadius: "var(--iris-radius-lg, 8px)",
1151
+ padding: "32px",
1152
+ boxShadow: "0 6px 20px -8px rgba(0, 0, 0, 0.16)"
1153
+ }
1154
+ },
1155
+ [
1156
+ vue.h(IrisStack, { spacing: "lg" }, () => [
1157
+ slots.header ? slots.header() : vue.h("div", { style: { textAlign: "center" } }, [
1158
+ vue.h(
1159
+ "h1",
1160
+ {
1161
+ style: {
1162
+ margin: "0 0 4px 0",
1163
+ fontSize: "22px",
1164
+ fontWeight: "700"
1165
+ }
1166
+ },
1167
+ props.title
1168
+ ),
1169
+ props.description ? vue.h(
1170
+ "p",
1171
+ {
1172
+ style: {
1173
+ margin: "0",
1174
+ color: "var(--iris-muted)",
1175
+ fontSize: "14px"
1176
+ }
1177
+ },
1178
+ props.description
1179
+ ) : null
1180
+ ]),
1181
+ props.error ? vue.h(IrisAlert, { tone: "danger", title: props.error }) : null,
1182
+ vue.h(
1183
+ IrisFormField,
1184
+ { label: "Email", required: true },
1185
+ () => vue.h(IrisInput, {
1186
+ type: "email",
1187
+ modelValue: email.value,
1188
+ "onUpdate:modelValue": (v) => email.value = v,
1189
+ placeholder: "you@example.com",
1190
+ disabled: props.loading,
1191
+ autocomplete: "email"
1192
+ })
1193
+ ),
1194
+ vue.h(
1195
+ IrisFormField,
1196
+ { label: "Password", required: true },
1197
+ () => vue.h(IrisPasswordInput, {
1198
+ modelValue: password.value,
1199
+ "onUpdate:modelValue": (v) => password.value = v,
1200
+ placeholder: "Enter your password",
1201
+ disabled: props.loading,
1202
+ autocomplete: "current-password"
1203
+ })
1204
+ ),
1205
+ props.showRemember ? vue.h(
1206
+ "div",
1207
+ {
1208
+ style: {
1209
+ display: "flex",
1210
+ alignItems: "center",
1211
+ justifyContent: "space-between"
1212
+ }
1213
+ },
1214
+ [
1215
+ vue.h(
1216
+ IrisCheckbox,
1217
+ {
1218
+ modelValue: remember.value,
1219
+ "onUpdate:modelValue": (v) => {
1220
+ remember.value = v === true;
1221
+ },
1222
+ disabled: props.loading
1223
+ },
1224
+ () => "Remember me"
1225
+ ),
1226
+ slots.forgot ? slots.forgot() : null
1227
+ ]
1228
+ ) : null,
1229
+ vue.h(
1230
+ IrisButton,
1231
+ {
1232
+ type: "submit",
1233
+ variant: "solid",
1234
+ loading: props.loading,
1235
+ style: { width: "100%" }
1236
+ },
1237
+ () => props.submitLabel
1238
+ ),
1239
+ slots.footer ? vue.h("div", null, [vue.h(IrisDivider, { spacing: "md", label: "or" }), slots.footer()]) : null
1240
+ ])
1241
+ ]
1242
+ )
1243
+ ])
1244
+ );
1245
+ }
1246
+ });
1247
+ var IrisSidebarLayout = vue.defineComponent({
1248
+ name: "IrisSidebarLayout",
1249
+ inheritAttrs: false,
1250
+ props: {
1251
+ collapsed: { type: Boolean, default: void 0 },
1252
+ defaultCollapsed: { type: Boolean, default: false },
1253
+ /** Sidebar width when expanded (px or CSS length). */
1254
+ width: { type: [Number, String], default: 240 },
1255
+ /** Sidebar width when collapsed. */
1256
+ collapsedWidth: { type: [Number, String], default: 60 },
1257
+ /** Sidebar position. */
1258
+ side: { type: String, default: "left" }
1259
+ },
1260
+ emits: {
1261
+ "update:collapsed": (_value) => true
1262
+ },
1263
+ setup(props, { slots, attrs, emit }) {
1264
+ const isControlled = vue.computed(() => props.collapsed !== void 0);
1265
+ const internalCollapsed = vue.ref(props.defaultCollapsed);
1266
+ const collapsed = vue.computed(
1267
+ () => isControlled.value ? Boolean(props.collapsed) : internalCollapsed.value
1268
+ );
1269
+ vue.watch(
1270
+ () => props.collapsed,
1271
+ (value) => {
1272
+ if (value !== void 0) internalCollapsed.value = value;
1273
+ }
1274
+ );
1275
+ const setCollapsed = (value) => {
1276
+ if (!isControlled.value) internalCollapsed.value = value;
1277
+ emit("update:collapsed", value);
1278
+ };
1279
+ const asLen = (v) => typeof v === "number" ? `${v}px` : v;
1280
+ return () => {
1281
+ const sidebarStyle = {
1282
+ width: collapsed.value ? asLen(props.collapsedWidth) : asLen(props.width),
1283
+ flexShrink: "0",
1284
+ background: "var(--iris-surface)",
1285
+ borderRight: props.side === "left" ? "1px solid var(--iris-border)" : "none",
1286
+ borderLeft: props.side === "right" ? "1px solid var(--iris-border)" : "none",
1287
+ transition: "width 180ms ease",
1288
+ overflow: "hidden",
1289
+ display: "flex",
1290
+ flexDirection: "column"
1291
+ };
1292
+ const mainStyle = {
1293
+ flex: "1",
1294
+ minWidth: "0",
1295
+ overflow: "auto",
1296
+ background: "var(--iris-background)"
1297
+ };
1298
+ const direction = props.side === "right" ? "row-reverse" : "row";
1299
+ return vue.h(
1300
+ "div",
1301
+ {
1302
+ ...attrs,
1303
+ "data-iris-sidebar-layout": "",
1304
+ "data-collapsed": collapsed.value ? "" : void 0,
1305
+ "data-side": props.side,
1306
+ style: {
1307
+ display: "flex",
1308
+ flexDirection: direction,
1309
+ width: "100%",
1310
+ height: "100%",
1311
+ minHeight: "0",
1312
+ color: "var(--iris-foreground)",
1313
+ ...attrs.style ?? {}
1314
+ }
1315
+ },
1316
+ [
1317
+ vue.h(
1318
+ "aside",
1319
+ {
1320
+ role: "complementary",
1321
+ "data-iris-sidebar": "",
1322
+ "data-collapsed": collapsed.value ? "" : void 0,
1323
+ style: sidebarStyle
1324
+ },
1325
+ slots.sidebar?.({ collapsed: collapsed.value, setCollapsed })
1326
+ ),
1327
+ vue.h("div", { "data-iris-sidebar-main": "", style: mainStyle }, slots.default?.())
1328
+ ]
1329
+ );
1330
+ };
1331
+ }
1332
+ });
1333
+ var IrisHeaderLayout = vue.defineComponent({
1334
+ name: "IrisHeaderLayout",
1335
+ inheritAttrs: false,
1336
+ props: {
1337
+ /** Header height (px or CSS length). Default `'auto'`. */
1338
+ headerHeight: { type: [Number, String], default: "auto" },
1339
+ /** Footer height (px or CSS length). Default `'auto'`. */
1340
+ footerHeight: { type: [Number, String], default: "auto" },
1341
+ /** When true, header sticks via `position: sticky` instead of static. */
1342
+ sticky: { type: Boolean, default: true }
1343
+ },
1344
+ setup(props, { slots, attrs }) {
1345
+ const asLen = (v) => typeof v === "number" ? `${v}px` : v;
1346
+ return () => vue.h(
1347
+ "div",
1348
+ {
1349
+ ...attrs,
1350
+ "data-iris-header-layout": "",
1351
+ style: {
1352
+ display: "flex",
1353
+ flexDirection: "column",
1354
+ width: "100%",
1355
+ height: "100%",
1356
+ minHeight: "0",
1357
+ background: "var(--iris-background)",
1358
+ color: "var(--iris-foreground)",
1359
+ ...attrs.style ?? {}
1360
+ }
1361
+ },
1362
+ [
1363
+ slots.header ? vue.h(
1364
+ "header",
1365
+ {
1366
+ role: "banner",
1367
+ "data-iris-header": "",
1368
+ style: {
1369
+ flexShrink: "0",
1370
+ height: asLen(props.headerHeight),
1371
+ borderBottom: "1px solid var(--iris-border)",
1372
+ background: "var(--iris-surface)",
1373
+ position: props.sticky ? "sticky" : "static",
1374
+ top: "0",
1375
+ zIndex: "50"
1376
+ }
1377
+ },
1378
+ slots.header()
1379
+ ) : null,
1380
+ vue.h(
1381
+ "main",
1382
+ {
1383
+ role: "main",
1384
+ "data-iris-header-main": "",
1385
+ style: { flex: "1", minHeight: "0", overflow: "auto" }
1386
+ },
1387
+ slots.default?.()
1388
+ ),
1389
+ slots.footer ? vue.h(
1390
+ "footer",
1391
+ {
1392
+ role: "contentinfo",
1393
+ "data-iris-footer": "",
1394
+ style: {
1395
+ flexShrink: "0",
1396
+ height: asLen(props.footerHeight),
1397
+ borderTop: "1px solid var(--iris-border)",
1398
+ background: "var(--iris-surface)"
1399
+ }
1400
+ },
1401
+ slots.footer()
1402
+ ) : null
1403
+ ]
1404
+ );
1405
+ }
1406
+ });
1407
+ var IrisDashboardGrid = vue.defineComponent({
1408
+ name: "IrisDashboardGrid",
1409
+ inheritAttrs: false,
1410
+ props: {
1411
+ columns: { type: Number, default: 12 },
1412
+ gap: { type: [Number, String], default: 16 },
1413
+ /** Minimum column width via `grid-template-columns: repeat(auto-fill, minmax(...))`. */
1414
+ minColWidth: { type: [Number, String], default: void 0 }
1415
+ },
1416
+ setup(props, { slots, attrs }) {
1417
+ const gap = typeof props.gap === "number" ? `${props.gap}px` : props.gap;
1418
+ return () => {
1419
+ const gridTemplateColumns = props.minColWidth ? `repeat(auto-fill, minmax(${typeof props.minColWidth === "number" ? `${props.minColWidth}px` : props.minColWidth}, 1fr))` : `repeat(${props.columns}, 1fr)`;
1420
+ return vue.h(
1421
+ "div",
1422
+ {
1423
+ ...attrs,
1424
+ "data-iris-dashboard-grid": "",
1425
+ style: {
1426
+ display: "grid",
1427
+ gap,
1428
+ gridTemplateColumns,
1429
+ width: "100%",
1430
+ ...attrs.style ?? {}
1431
+ }
1432
+ },
1433
+ slots.default?.()
1434
+ );
1435
+ };
1436
+ }
1437
+ });
1438
+ var IrisDashboardCard = vue.defineComponent({
1439
+ name: "IrisDashboardCard",
1440
+ inheritAttrs: false,
1441
+ props: {
1442
+ colSpan: { type: [Number, String], default: 1 },
1443
+ rowSpan: { type: Number, default: 1 },
1444
+ /** Show a styled card surface (background + border). Default `true`. */
1445
+ surface: { type: Boolean, default: true }
1446
+ },
1447
+ setup(props, { slots, attrs }) {
1448
+ return () => {
1449
+ const colSpan = props.colSpan === "full" ? "1 / -1" : `span ${props.colSpan}`;
1450
+ const rowSpan = props.rowSpan > 1 ? `span ${props.rowSpan}` : void 0;
1451
+ return vue.h(
1452
+ "div",
1453
+ {
1454
+ ...attrs,
1455
+ "data-iris-dashboard-card": "",
1456
+ style: {
1457
+ gridColumn: colSpan,
1458
+ gridRow: rowSpan,
1459
+ background: props.surface ? "var(--iris-surface)" : "transparent",
1460
+ border: props.surface ? "1px solid var(--iris-border)" : "none",
1461
+ borderRadius: "var(--iris-radius-md)",
1462
+ padding: props.surface ? "var(--iris-padding-lg)" : "0",
1463
+ minWidth: "0",
1464
+ ...attrs.style ?? {}
1465
+ }
1466
+ },
1467
+ slots.default?.()
1468
+ );
1469
+ };
1470
+ }
1471
+ });
1472
+
1473
+ // src/skeletons/DashboardTemplate.ts
1474
+ var IrisDashboardTemplate = vue.defineComponent({
1475
+ name: "IrisDashboardTemplate",
1476
+ inheritAttrs: false,
1477
+ props: {
1478
+ title: { type: String, default: "Dashboard" },
1479
+ sidebarTitle: { type: String, default: "" },
1480
+ nav: { type: Array, default: () => [] },
1481
+ /** Currently active nav item id. */
1482
+ activeId: { type: String, default: "" },
1483
+ /** Cards to render in the main grid. Skip to use the `default` slot only. */
1484
+ cards: { type: Array, default: () => [] },
1485
+ defaultCollapsed: { type: Boolean, default: false }
1486
+ },
1487
+ emits: {
1488
+ "update:activeId": (_id) => true,
1489
+ "update:collapsed": (_value) => true
1490
+ },
1491
+ setup(props, { attrs, slots, emit }) {
1492
+ const collapsed = vue.ref(props.defaultCollapsed);
1493
+ const onSelectNav = (id) => {
1494
+ emit("update:activeId", id);
1495
+ };
1496
+ return () => vue.h(
1497
+ "div",
1498
+ {
1499
+ ...attrs,
1500
+ "data-iris-dashboard-template": "",
1501
+ style: {
1502
+ width: "100%",
1503
+ height: "100vh",
1504
+ background: "var(--iris-background)",
1505
+ color: "var(--iris-foreground)",
1506
+ ...attrs.style ?? {}
1507
+ }
1508
+ },
1509
+ vue.h(
1510
+ IrisSidebarLayout,
1511
+ {
1512
+ collapsed: collapsed.value,
1513
+ "onUpdate:collapsed": (v) => {
1514
+ collapsed.value = v;
1515
+ emit("update:collapsed", v);
1516
+ },
1517
+ width: 240,
1518
+ collapsedWidth: 64
1519
+ },
1520
+ {
1521
+ sidebar: (state) => vue.h(IrisStack, { spacing: "sm", style: { padding: "12px" } }, () => [
1522
+ slots["sidebar-header"] ? slots["sidebar-header"](state) : props.sidebarTitle ? vue.h(
1523
+ "div",
1524
+ {
1525
+ style: {
1526
+ padding: "8px 12px",
1527
+ fontSize: "14px",
1528
+ fontWeight: "600",
1529
+ color: "var(--iris-foreground)",
1530
+ opacity: state.collapsed ? 0 : 1,
1531
+ transition: "opacity 120ms ease",
1532
+ whiteSpace: "nowrap",
1533
+ overflow: "hidden"
1534
+ }
1535
+ },
1536
+ props.sidebarTitle
1537
+ ) : null,
1538
+ vue.h(
1539
+ "nav",
1540
+ {
1541
+ "aria-label": "Primary",
1542
+ style: { display: "flex", flexDirection: "column", gap: "2px" }
1543
+ },
1544
+ props.nav.map((item) => {
1545
+ const isActive = item.id === props.activeId;
1546
+ return vue.h(
1547
+ "button",
1548
+ {
1549
+ key: item.id,
1550
+ type: "button",
1551
+ "data-iris-dashboard-nav-item": item.id,
1552
+ "data-iris-dashboard-nav-active": isActive ? "true" : void 0,
1553
+ onClick: () => onSelectNav(item.id),
1554
+ style: {
1555
+ display: "flex",
1556
+ alignItems: "center",
1557
+ gap: "10px",
1558
+ padding: "8px 12px",
1559
+ background: isActive ? "var(--iris-primary)" : "transparent",
1560
+ color: isActive ? "var(--iris-primary-foreground, #fff)" : "var(--iris-foreground)",
1561
+ border: "none",
1562
+ borderRadius: "var(--iris-radius-sm, 4px)",
1563
+ cursor: "pointer",
1564
+ fontSize: "14px",
1565
+ fontFamily: "inherit",
1566
+ textAlign: "start",
1567
+ outline: "none"
1568
+ }
1569
+ },
1570
+ [
1571
+ item.icon ? vue.h(
1572
+ "span",
1573
+ {
1574
+ "aria-hidden": "true",
1575
+ style: {
1576
+ width: "16px",
1577
+ display: "inline-flex",
1578
+ justifyContent: "center"
1579
+ }
1580
+ },
1581
+ item.icon
1582
+ ) : null,
1583
+ vue.h(
1584
+ "span",
1585
+ {
1586
+ style: {
1587
+ opacity: state.collapsed ? 0 : 1,
1588
+ transition: "opacity 120ms ease",
1589
+ whiteSpace: "nowrap"
1590
+ }
1591
+ },
1592
+ item.label
1593
+ )
1594
+ ]
1595
+ );
1596
+ })
1597
+ )
1598
+ ]),
1599
+ default: () => vue.h(IrisHeaderLayout, {}, {
1600
+ header: () => slots.header ? slots.header() : vue.h(
1601
+ "div",
1602
+ {
1603
+ style: {
1604
+ padding: "12px 20px",
1605
+ fontSize: "16px",
1606
+ fontWeight: "600"
1607
+ }
1608
+ },
1609
+ props.title
1610
+ ),
1611
+ default: () => slots.default ? slots.default() : vue.h(
1612
+ "div",
1613
+ { style: { padding: "20px" } },
1614
+ vue.h(
1615
+ IrisDashboardGrid,
1616
+ { columns: 12, gap: 16 },
1617
+ () => props.cards.map(
1618
+ (card) => vue.h(
1619
+ IrisDashboardCard,
1620
+ {
1621
+ key: card.id,
1622
+ colSpan: card.colSpan ?? 4,
1623
+ rowSpan: card.rowSpan ?? 1,
1624
+ "data-iris-dashboard-card-id": card.id
1625
+ },
1626
+ () => [
1627
+ vue.h(
1628
+ "h3",
1629
+ {
1630
+ style: {
1631
+ margin: "0 0 8px 0",
1632
+ fontSize: "14px",
1633
+ fontWeight: "600",
1634
+ color: "var(--iris-foreground)"
1635
+ }
1636
+ },
1637
+ card.title
1638
+ ),
1639
+ slots[`card.${card.id}`] ? slots[`card.${card.id}`]() : card.body ? vue.h(
1640
+ "div",
1641
+ { style: { fontSize: "13px", color: "var(--iris-muted)" } },
1642
+ card.body
1643
+ ) : null
1644
+ ]
1645
+ )
1646
+ )
1647
+ )
1648
+ )
1649
+ })
1650
+ }
1651
+ )
1652
+ );
1653
+ }
1654
+ });
1655
+
1656
+ exports.IrisDashboardTemplate = IrisDashboardTemplate;
1657
+ exports.IrisLoginTemplate = IrisLoginTemplate;
1658
+ //# sourceMappingURL=skeletons.cjs.map
1659
+ //# sourceMappingURL=skeletons.cjs.map