@iris-ui-kit/react 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 (154) hide show
  1. package/LICENSE +21 -0
  2. package/dist/SidebarLayout-fWQRvK0a.d.cts +26 -0
  3. package/dist/SidebarLayout-fWQRvK0a.d.ts +26 -0
  4. package/dist/admin.cjs +1583 -0
  5. package/dist/admin.cjs.map +1 -0
  6. package/dist/admin.d.cts +183 -0
  7. package/dist/admin.d.ts +183 -0
  8. package/dist/admin.js +9 -0
  9. package/dist/admin.js.map +1 -0
  10. package/dist/async.cjs +97 -0
  11. package/dist/async.cjs.map +1 -0
  12. package/dist/async.d.cts +57 -0
  13. package/dist/async.d.ts +57 -0
  14. package/dist/async.js +5 -0
  15. package/dist/async.js.map +1 -0
  16. package/dist/behaviors.cjs +569 -0
  17. package/dist/behaviors.cjs.map +1 -0
  18. package/dist/behaviors.d.cts +188 -0
  19. package/dist/behaviors.d.ts +188 -0
  20. package/dist/behaviors.js +4 -0
  21. package/dist/behaviors.js.map +1 -0
  22. package/dist/chunk-2TQSTINK.js +541 -0
  23. package/dist/chunk-2TQSTINK.js.map +1 -0
  24. package/dist/chunk-53KFLVIZ.js +68 -0
  25. package/dist/chunk-53KFLVIZ.js.map +1 -0
  26. package/dist/chunk-6BMFMCMQ.js +140 -0
  27. package/dist/chunk-6BMFMCMQ.js.map +1 -0
  28. package/dist/chunk-AUA5GBT6.js +143 -0
  29. package/dist/chunk-AUA5GBT6.js.map +1 -0
  30. package/dist/chunk-DE6V6NOI.js +122 -0
  31. package/dist/chunk-DE6V6NOI.js.map +1 -0
  32. package/dist/chunk-HEJGN6UI.js +78 -0
  33. package/dist/chunk-HEJGN6UI.js.map +1 -0
  34. package/dist/chunk-IDD35UJ6.js +83 -0
  35. package/dist/chunk-IDD35UJ6.js.map +1 -0
  36. package/dist/chunk-J5FB2UB2.js +166 -0
  37. package/dist/chunk-J5FB2UB2.js.map +1 -0
  38. package/dist/chunk-OMOTUHYI.js +66 -0
  39. package/dist/chunk-OMOTUHYI.js.map +1 -0
  40. package/dist/chunk-RZNYIFV2.js +34 -0
  41. package/dist/chunk-RZNYIFV2.js.map +1 -0
  42. package/dist/chunk-SJO74IB3.js +54 -0
  43. package/dist/chunk-SJO74IB3.js.map +1 -0
  44. package/dist/chunk-SORLRLS4.js +53 -0
  45. package/dist/chunk-SORLRLS4.js.map +1 -0
  46. package/dist/chunk-TO7YVSAI.js +64 -0
  47. package/dist/chunk-TO7YVSAI.js.map +1 -0
  48. package/dist/chunk-TQ33XJIK.js +22 -0
  49. package/dist/chunk-TQ33XJIK.js.map +1 -0
  50. package/dist/chunk-UWUUEZCD.js +950 -0
  51. package/dist/chunk-UWUUEZCD.js.map +1 -0
  52. package/dist/chunk-VWCJJRSC.js +1200 -0
  53. package/dist/chunk-VWCJJRSC.js.map +1 -0
  54. package/dist/chunk-WIHBIDVJ.js +54 -0
  55. package/dist/chunk-WIHBIDVJ.js.map +1 -0
  56. package/dist/chunk-XCK4RIXB.js +66 -0
  57. package/dist/chunk-XCK4RIXB.js.map +1 -0
  58. package/dist/chunk-YAHXR2CJ.js +48 -0
  59. package/dist/chunk-YAHXR2CJ.js.map +1 -0
  60. package/dist/chunk-YXIO5THG.js +4 -0
  61. package/dist/chunk-YXIO5THG.js.map +1 -0
  62. package/dist/chunk-ZKBDUWUD.js +156 -0
  63. package/dist/chunk-ZKBDUWUD.js.map +1 -0
  64. package/dist/data.cjs +112 -0
  65. package/dist/data.cjs.map +1 -0
  66. package/dist/data.d.cts +53 -0
  67. package/dist/data.d.ts +53 -0
  68. package/dist/data.js +5 -0
  69. package/dist/data.js.map +1 -0
  70. package/dist/error-boundary.cjs +106 -0
  71. package/dist/error-boundary.cjs.map +1 -0
  72. package/dist/error-boundary.d.cts +41 -0
  73. package/dist/error-boundary.d.ts +41 -0
  74. package/dist/error-boundary.js +6 -0
  75. package/dist/error-boundary.js.map +1 -0
  76. package/dist/floating.cjs +163 -0
  77. package/dist/floating.cjs.map +1 -0
  78. package/dist/floating.d.cts +92 -0
  79. package/dist/floating.d.ts +92 -0
  80. package/dist/floating.js +5 -0
  81. package/dist/floating.js.map +1 -0
  82. package/dist/form.cjs +159 -0
  83. package/dist/form.cjs.map +1 -0
  84. package/dist/form.d.cts +120 -0
  85. package/dist/form.d.ts +120 -0
  86. package/dist/form.js +5 -0
  87. package/dist/form.js.map +1 -0
  88. package/dist/i18n.cjs +100 -0
  89. package/dist/i18n.cjs.map +1 -0
  90. package/dist/i18n.d.cts +50 -0
  91. package/dist/i18n.d.ts +50 -0
  92. package/dist/i18n.js +5 -0
  93. package/dist/i18n.js.map +1 -0
  94. package/dist/index.cjs +16635 -0
  95. package/dist/index.cjs.map +1 -0
  96. package/dist/index.d.cts +2964 -0
  97. package/dist/index.d.ts +2964 -0
  98. package/dist/index.js +12217 -0
  99. package/dist/index.js.map +1 -0
  100. package/dist/layouts.cjs +386 -0
  101. package/dist/layouts.cjs.map +1 -0
  102. package/dist/layouts.d.cts +92 -0
  103. package/dist/layouts.d.ts +92 -0
  104. package/dist/layouts.js +6 -0
  105. package/dist/layouts.js.map +1 -0
  106. package/dist/modal-utils.cjs +168 -0
  107. package/dist/modal-utils.cjs.map +1 -0
  108. package/dist/modal-utils.d.cts +43 -0
  109. package/dist/modal-utils.d.ts +43 -0
  110. package/dist/modal-utils.js +4 -0
  111. package/dist/modal-utils.js.map +1 -0
  112. package/dist/motion.cjs +104 -0
  113. package/dist/motion.cjs.map +1 -0
  114. package/dist/motion.d.cts +41 -0
  115. package/dist/motion.d.ts +41 -0
  116. package/dist/motion.js +4 -0
  117. package/dist/motion.js.map +1 -0
  118. package/dist/provider.cjs +191 -0
  119. package/dist/provider.cjs.map +1 -0
  120. package/dist/provider.d.cts +62 -0
  121. package/dist/provider.d.ts +62 -0
  122. package/dist/provider.js +8 -0
  123. package/dist/provider.js.map +1 -0
  124. package/dist/resource.cjs +61 -0
  125. package/dist/resource.cjs.map +1 -0
  126. package/dist/resource.d.cts +21 -0
  127. package/dist/resource.d.ts +21 -0
  128. package/dist/resource.js +5 -0
  129. package/dist/resource.js.map +1 -0
  130. package/dist/skeletons.cjs +1304 -0
  131. package/dist/skeletons.cjs.map +1 -0
  132. package/dist/skeletons.d.cts +70 -0
  133. package/dist/skeletons.d.ts +70 -0
  134. package/dist/skeletons.js +8 -0
  135. package/dist/skeletons.js.map +1 -0
  136. package/dist/skins.cjs +134 -0
  137. package/dist/skins.cjs.map +1 -0
  138. package/dist/skins.d.cts +45 -0
  139. package/dist/skins.d.ts +45 -0
  140. package/dist/skins.js +5 -0
  141. package/dist/skins.js.map +1 -0
  142. package/dist/theme.cjs +100 -0
  143. package/dist/theme.cjs.map +1 -0
  144. package/dist/theme.d.cts +71 -0
  145. package/dist/theme.d.ts +71 -0
  146. package/dist/theme.js +5 -0
  147. package/dist/theme.js.map +1 -0
  148. package/dist/undo.cjs +73 -0
  149. package/dist/undo.cjs.map +1 -0
  150. package/dist/undo.d.cts +47 -0
  151. package/dist/undo.d.ts +47 -0
  152. package/dist/undo.js +4 -0
  153. package/dist/undo.js.map +1 -0
  154. package/package.json +61 -0
package/dist/admin.cjs ADDED
@@ -0,0 +1,1583 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ var React13 = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var core = require('@iris-ui-kit/core');
7
+ var icons = require('@iris-ui-kit/icons');
8
+ require('@iris-ui-kit/theme');
9
+ var reactDom = require('react-dom');
10
+ var dom = require('@floating-ui/dom');
11
+
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
14
+ var n = Object.create(null);
15
+ if (e) {
16
+ Object.keys(e).forEach(function (k) {
17
+ if (k !== 'default') {
18
+ var d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: function () { return e[k]; }
22
+ });
23
+ }
24
+ });
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+
30
+ var React13__namespace = /*#__PURE__*/_interopNamespace(React13);
31
+
32
+ // src/admin/AdminLayout.tsx
33
+ function asLen(v) {
34
+ return typeof v === "number" ? `${v}px` : v;
35
+ }
36
+ function IrisSidebarLayout({
37
+ collapsed: collapsedProp,
38
+ defaultCollapsed = false,
39
+ onCollapsedChange,
40
+ width = 240,
41
+ collapsedWidth = 60,
42
+ side = "left",
43
+ sidebar,
44
+ style,
45
+ children,
46
+ ...rest
47
+ }) {
48
+ const isControlled = collapsedProp !== void 0;
49
+ const [internal, setInternal] = React13__namespace.useState(defaultCollapsed);
50
+ const collapsed = isControlled ? Boolean(collapsedProp) : internal;
51
+ const setCollapsed = React13__namespace.useCallback(
52
+ (next) => {
53
+ if (!isControlled) setInternal(next);
54
+ onCollapsedChange?.(next);
55
+ },
56
+ [isControlled, onCollapsedChange]
57
+ );
58
+ const sidebarContent = typeof sidebar === "function" ? sidebar({
59
+ collapsed,
60
+ setCollapsed
61
+ }) : sidebar;
62
+ const sidebarStyle = {
63
+ width: collapsed ? asLen(collapsedWidth) : asLen(width),
64
+ flexShrink: 0,
65
+ background: "var(--iris-surface)",
66
+ borderRight: side === "left" ? "1px solid var(--iris-border)" : "none",
67
+ borderLeft: side === "right" ? "1px solid var(--iris-border)" : "none",
68
+ transition: "width 180ms ease",
69
+ overflow: "hidden",
70
+ display: "flex",
71
+ flexDirection: "column"
72
+ };
73
+ return /* @__PURE__ */ jsxRuntime.jsxs(
74
+ "div",
75
+ {
76
+ ...rest,
77
+ "data-iris-sidebar-layout": "",
78
+ "data-collapsed": collapsed ? "" : void 0,
79
+ "data-side": side,
80
+ style: {
81
+ display: "flex",
82
+ flexDirection: side === "right" ? "row-reverse" : "row",
83
+ width: "100%",
84
+ height: "100%",
85
+ minHeight: 0,
86
+ color: "var(--iris-foreground)",
87
+ ...style
88
+ },
89
+ children: [
90
+ /* @__PURE__ */ jsxRuntime.jsx(
91
+ "aside",
92
+ {
93
+ role: "complementary",
94
+ "data-iris-sidebar": "",
95
+ "data-collapsed": collapsed ? "" : void 0,
96
+ style: sidebarStyle,
97
+ children: sidebarContent
98
+ }
99
+ ),
100
+ /* @__PURE__ */ jsxRuntime.jsx(
101
+ "div",
102
+ {
103
+ "data-iris-sidebar-main": "",
104
+ style: {
105
+ flex: 1,
106
+ minWidth: 0,
107
+ overflow: "auto",
108
+ background: "var(--iris-background)"
109
+ },
110
+ children
111
+ }
112
+ )
113
+ ]
114
+ }
115
+ );
116
+ }
117
+ function asLen2(v) {
118
+ return typeof v === "number" ? `${v}px` : v;
119
+ }
120
+ function IrisHeaderLayout({
121
+ headerHeight = "auto",
122
+ footerHeight = "auto",
123
+ sticky = true,
124
+ header,
125
+ footer,
126
+ style,
127
+ children,
128
+ ...rest
129
+ }) {
130
+ return /* @__PURE__ */ jsxRuntime.jsxs(
131
+ "div",
132
+ {
133
+ ...rest,
134
+ "data-iris-header-layout": "",
135
+ style: {
136
+ display: "flex",
137
+ flexDirection: "column",
138
+ width: "100%",
139
+ height: "100%",
140
+ minHeight: 0,
141
+ background: "var(--iris-background)",
142
+ color: "var(--iris-foreground)",
143
+ ...style
144
+ },
145
+ children: [
146
+ header ? /* @__PURE__ */ jsxRuntime.jsx(
147
+ "header",
148
+ {
149
+ role: "banner",
150
+ "data-iris-header": "",
151
+ style: {
152
+ flexShrink: 0,
153
+ height: asLen2(headerHeight),
154
+ borderBottom: "1px solid var(--iris-border)",
155
+ background: "var(--iris-surface)",
156
+ position: sticky ? "sticky" : "static",
157
+ top: 0,
158
+ zIndex: 50
159
+ },
160
+ children: header
161
+ }
162
+ ) : null,
163
+ /* @__PURE__ */ jsxRuntime.jsx(
164
+ "main",
165
+ {
166
+ role: "main",
167
+ "data-iris-header-main": "",
168
+ style: { flex: 1, minHeight: 0, overflow: "auto" },
169
+ children
170
+ }
171
+ ),
172
+ footer ? /* @__PURE__ */ jsxRuntime.jsx(
173
+ "footer",
174
+ {
175
+ role: "contentinfo",
176
+ "data-iris-footer": "",
177
+ style: {
178
+ flexShrink: 0,
179
+ height: asLen2(footerHeight),
180
+ borderTop: "1px solid var(--iris-border)",
181
+ background: "var(--iris-surface)"
182
+ },
183
+ children: footer
184
+ }
185
+ ) : null
186
+ ]
187
+ }
188
+ );
189
+ }
190
+ function useStore(store) {
191
+ return React13.useSyncExternalStore(
192
+ store.subscribe,
193
+ store.getState,
194
+ store.getState
195
+ // SSR fallback — same as client snapshot in our case.
196
+ );
197
+ }
198
+ var IrisThemeContext = React13.createContext(null);
199
+ function useThemeOptional() {
200
+ return React13.useContext(IrisThemeContext)?.current;
201
+ }
202
+ function IrisIcon({
203
+ name,
204
+ size = 24,
205
+ strokeWidth = 2,
206
+ fill = false,
207
+ title,
208
+ registry = icons.defaultIconRegistry,
209
+ style,
210
+ ...rest
211
+ }) {
212
+ const theme = useThemeOptional();
213
+ const icon = icons.resolveThemedIcon(registry, name, theme);
214
+ if (!icon) return null;
215
+ const paint = fill ? { fill: "currentColor" } : {
216
+ fill: "none",
217
+ stroke: "currentColor",
218
+ strokeWidth,
219
+ strokeLinecap: "round",
220
+ strokeLinejoin: "round"
221
+ };
222
+ return /* @__PURE__ */ jsxRuntime.jsxs(
223
+ "svg",
224
+ {
225
+ ...rest,
226
+ xmlns: "http://www.w3.org/2000/svg",
227
+ viewBox: icon.viewBox ?? "0 0 24 24",
228
+ width: size,
229
+ height: size,
230
+ ...paint,
231
+ role: title ? "img" : void 0,
232
+ "aria-label": title || void 0,
233
+ "aria-hidden": title ? void 0 : true,
234
+ "data-iris-icon": name,
235
+ style: { display: "inline-block", verticalAlign: "middle", flexShrink: 0, ...style },
236
+ children: [
237
+ title ? /* @__PURE__ */ jsxRuntime.jsx("title", { children: title }) : null,
238
+ icon.nodes.map((node, i) => React13__namespace.createElement(node.tag, { key: i, ...node.attrs }))
239
+ ]
240
+ }
241
+ );
242
+ }
243
+ var I18nContext = React13__namespace.createContext(null);
244
+ var fallbackI18n = null;
245
+ function getFallback() {
246
+ if (fallbackI18n === null) fallbackI18n = core.createI18n();
247
+ return fallbackI18n;
248
+ }
249
+ function useI18n() {
250
+ const ctx = React13__namespace.useContext(I18nContext);
251
+ const i18n = ctx ?? getFallback();
252
+ const state = useStore(i18n.store);
253
+ return {
254
+ locale: state.locale,
255
+ t: i18n.t,
256
+ formatDate: i18n.formatDate,
257
+ formatNumber: i18n.formatNumber,
258
+ formatRelativeTime: i18n.formatRelativeTime,
259
+ setLocale: i18n.setLocale,
260
+ setMessages: i18n.setMessages
261
+ };
262
+ }
263
+ function IrisNavMenu({
264
+ items,
265
+ activeKey,
266
+ expandedKeys,
267
+ defaultExpandedKeys,
268
+ collapsed = false,
269
+ ariaLabel,
270
+ onSelect,
271
+ onExpandedKeysChange
272
+ }) {
273
+ const { t } = useI18n();
274
+ const trailOf = React13__namespace.useCallback(
275
+ (key) => key ? core.branchTrail(items, key) : [],
276
+ [items]
277
+ );
278
+ const isControlled = expandedKeys !== void 0;
279
+ const modelRef = React13__namespace.useRef(null);
280
+ if (modelRef.current === null) {
281
+ modelRef.current = core.createExpansion({
282
+ mode: "multiple",
283
+ defaultExpanded: defaultExpandedKeys ?? trailOf(activeKey)
284
+ });
285
+ }
286
+ const model = modelRef.current;
287
+ const internalExpanded = useStore(model.store);
288
+ const expanded = isControlled ? expandedKeys : internalExpanded;
289
+ React13__namespace.useEffect(() => {
290
+ if (isControlled) return;
291
+ model.merge(trailOf(activeKey));
292
+ }, [activeKey, isControlled, model, trailOf]);
293
+ const setExpanded = (keys) => {
294
+ if (!isControlled) model.set(keys);
295
+ onExpandedKeysChange?.(keys);
296
+ };
297
+ const toggle = (key) => setExpanded(expanded.includes(key) ? expanded.filter((k) => k !== key) : [...expanded, key]);
298
+ const select = (node) => {
299
+ if (node.disabled) return;
300
+ onSelect?.(node.key, node);
301
+ };
302
+ const [hovered, setHovered] = React13__namespace.useState(null);
303
+ const tree = core.visibleNav(items);
304
+ const activePath = activeKey ? core.findNavPath(items, activeKey).map((n) => n.key) : [];
305
+ const itemStyle = (opts) => ({
306
+ display: "flex",
307
+ alignItems: "center",
308
+ gap: 10,
309
+ width: "100%",
310
+ boxSizing: "border-box",
311
+ border: "none",
312
+ borderRadius: "var(--iris-radius-md, 6px)",
313
+ background: opts.active ? "var(--iris-primary)" : opts.hovered && !opts.disabled ? "var(--iris-surface)" : "transparent",
314
+ color: opts.active ? "var(--iris-primary-foreground, #fff)" : opts.trail ? "var(--iris-primary)" : "var(--iris-foreground)",
315
+ font: "inherit",
316
+ fontSize: 14,
317
+ fontWeight: opts.active || opts.trail ? 600 : 400,
318
+ textAlign: "start",
319
+ cursor: opts.disabled ? "not-allowed" : "pointer",
320
+ opacity: opts.disabled ? 0.5 : 1,
321
+ padding: collapsed ? 10 : `8px 10px 8px ${12 + opts.depth * 16}px`,
322
+ justifyContent: collapsed ? "center" : "flex-start"
323
+ });
324
+ const iconNode = (node, size = 18) => node.icon ? /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: node.icon, size }) : null;
325
+ const badgeNode = (node) => node.badge !== void 0 && node.badge !== "" && !collapsed ? /* @__PURE__ */ jsxRuntime.jsx(
326
+ "span",
327
+ {
328
+ "data-iris-nav-badge": "",
329
+ style: {
330
+ marginInlineStart: "auto",
331
+ fontSize: 11,
332
+ lineHeight: 1,
333
+ padding: "2px 6px",
334
+ borderRadius: 999,
335
+ background: "var(--iris-danger, #e5484d)",
336
+ color: "#fff"
337
+ },
338
+ children: String(node.badge)
339
+ }
340
+ ) : null;
341
+ const hoverProps = (key) => ({
342
+ onMouseEnter: () => setHovered(key),
343
+ onMouseLeave: () => setHovered((h) => h === key ? null : h)
344
+ });
345
+ const renderCollapsed = (node) => {
346
+ const branch = core.isBranch(node);
347
+ const active = branch ? activePath.includes(node.key) : node.key === activeKey;
348
+ return /* @__PURE__ */ jsxRuntime.jsx(
349
+ "button",
350
+ {
351
+ type: "button",
352
+ "data-iris-nav-item": "",
353
+ "data-key": node.key,
354
+ "data-active": active ? "true" : void 0,
355
+ "data-branch": branch ? "true" : void 0,
356
+ disabled: node.disabled,
357
+ title: node.title,
358
+ "aria-label": branch ? `${node.title} (section)` : node.title,
359
+ "aria-current": active && !branch ? "page" : void 0,
360
+ style: itemStyle({
361
+ depth: 0,
362
+ active,
363
+ trail: false,
364
+ disabled: !!node.disabled,
365
+ hovered: hovered === node.key
366
+ }),
367
+ ...hoverProps(node.key),
368
+ onClick: () => select(branch ? core.firstLeaf(node) : node),
369
+ children: iconNode(node, 20) ?? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 700 }, children: node.title.charAt(0) })
370
+ },
371
+ node.key
372
+ );
373
+ };
374
+ const renderItem = (node, depth) => {
375
+ const branch = core.isBranch(node);
376
+ const open = expanded.includes(node.key);
377
+ const active = node.key === activeKey;
378
+ const trail = branch && activePath.includes(node.key);
379
+ const row = /* @__PURE__ */ jsxRuntime.jsxs(
380
+ "button",
381
+ {
382
+ type: "button",
383
+ "data-iris-nav-item": "",
384
+ "data-key": node.key,
385
+ "data-branch": branch ? "true" : void 0,
386
+ "data-active": active ? "true" : void 0,
387
+ "data-open": branch && open ? "true" : void 0,
388
+ "data-depth": String(depth),
389
+ disabled: node.disabled,
390
+ "aria-expanded": branch ? open ? "true" : "false" : void 0,
391
+ "aria-current": active ? "page" : void 0,
392
+ style: itemStyle({
393
+ depth,
394
+ active,
395
+ trail,
396
+ disabled: !!node.disabled,
397
+ hovered: hovered === node.key
398
+ }),
399
+ ...hoverProps(node.key),
400
+ onClick: () => branch ? toggle(node.key) : select(node),
401
+ children: [
402
+ iconNode(node),
403
+ /* @__PURE__ */ jsxRuntime.jsx(
404
+ "span",
405
+ {
406
+ style: {
407
+ flex: branch ? 1 : "0 1 auto",
408
+ overflow: "hidden",
409
+ textOverflow: "ellipsis",
410
+ whiteSpace: "nowrap"
411
+ },
412
+ children: node.title
413
+ }
414
+ ),
415
+ badgeNode(node),
416
+ branch ? /* @__PURE__ */ jsxRuntime.jsx(
417
+ IrisIcon,
418
+ {
419
+ name: open ? "chevron-down" : "chevron-right",
420
+ size: 16,
421
+ style: { marginInlineStart: node.badge ? 6 : "auto", opacity: 0.6 }
422
+ }
423
+ ) : null
424
+ ]
425
+ }
426
+ );
427
+ if (!branch || !open) {
428
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-iris-nav-group": branch ? "" : void 0, children: row }, node.key);
429
+ }
430
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-iris-nav-group": "", "data-open": "true", children: [
431
+ row,
432
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "data-iris-nav-children": "", role: "group", children: (node.children ?? []).map((child) => renderItem(child, depth + 1)) })
433
+ ] }, node.key);
434
+ };
435
+ const onKeyDown = (e) => {
436
+ const root = e.currentTarget;
437
+ const buttons = Array.from(root.querySelectorAll("[data-iris-nav-item]"));
438
+ if (buttons.length === 0) return;
439
+ const idx = buttons.indexOf(document.activeElement);
440
+ const focusAt = (i) => buttons[(i + buttons.length) % buttons.length]?.focus();
441
+ switch (e.key) {
442
+ case "ArrowDown":
443
+ e.preventDefault();
444
+ return focusAt(idx < 0 ? 0 : idx + 1);
445
+ case "ArrowUp":
446
+ e.preventDefault();
447
+ return focusAt(idx < 0 ? buttons.length - 1 : idx - 1);
448
+ case "Home":
449
+ e.preventDefault();
450
+ return void buttons[0]?.focus();
451
+ case "End":
452
+ e.preventDefault();
453
+ return void buttons[buttons.length - 1]?.focus();
454
+ }
455
+ if (collapsed || idx < 0) return;
456
+ const key = buttons[idx]?.getAttribute("data-key");
457
+ if (!key) return;
458
+ const node = core.findNavNode(items, key);
459
+ if (e.key === "ArrowRight" && node && core.isBranch(node)) {
460
+ e.preventDefault();
461
+ if (!expanded.includes(key)) toggle(key);
462
+ else focusAt(idx + 1);
463
+ } else if (e.key === "ArrowLeft") {
464
+ if (node && core.isBranch(node) && expanded.includes(key)) {
465
+ e.preventDefault();
466
+ toggle(key);
467
+ } else {
468
+ const parentKey = core.findNavPath(items, key).at(-2)?.key;
469
+ if (parentKey) {
470
+ e.preventDefault();
471
+ buttons.find((b) => b.getAttribute("data-key") === parentKey)?.focus();
472
+ }
473
+ }
474
+ }
475
+ };
476
+ return /* @__PURE__ */ jsxRuntime.jsx(
477
+ "nav",
478
+ {
479
+ "data-iris-nav-menu": "",
480
+ "data-collapsed": collapsed ? "true" : void 0,
481
+ "aria-label": ariaLabel ?? t("admin.nav"),
482
+ onKeyDown,
483
+ style: { display: "flex", flexDirection: "column", gap: 2 },
484
+ children: collapsed ? tree.map((node) => renderCollapsed(node)) : tree.map((node) => renderItem(node, 0))
485
+ }
486
+ );
487
+ }
488
+ var IrisBreadcrumb = React13__namespace.forwardRef(
489
+ function IrisBreadcrumb2({ separator = "/", children, ...rest }, ref) {
490
+ const { t } = useI18n();
491
+ const flat = React13__namespace.Children.toArray(children).filter(
492
+ (c) => React13__namespace.isValidElement(c)
493
+ );
494
+ const total = flat.length;
495
+ return /* @__PURE__ */ jsxRuntime.jsx(
496
+ "nav",
497
+ {
498
+ ...rest,
499
+ ref,
500
+ "aria-label": t("breadcrumb.label"),
501
+ "data-iris-breadcrumb": "",
502
+ children: /* @__PURE__ */ jsxRuntime.jsx(
503
+ "ol",
504
+ {
505
+ style: {
506
+ display: "inline-flex",
507
+ alignItems: "center",
508
+ gap: 6,
509
+ margin: 0,
510
+ padding: 0,
511
+ listStyle: "none",
512
+ fontSize: 14
513
+ },
514
+ children: flat.map((child, i) => {
515
+ const isLast = i === total - 1;
516
+ const itemNode = /* @__PURE__ */ jsxRuntime.jsx(
517
+ "li",
518
+ {
519
+ "data-iris-breadcrumb-item": "",
520
+ "data-iris-breadcrumb-last": isLast ? "true" : void 0,
521
+ style: { display: "inline-flex", alignItems: "center" },
522
+ children: React13__namespace.cloneElement(child, {
523
+ isCurrent: isLast
524
+ })
525
+ },
526
+ `item-${i}`
527
+ );
528
+ const sepNode = !isLast ? /* @__PURE__ */ jsxRuntime.jsx(
529
+ "li",
530
+ {
531
+ "data-iris-breadcrumb-separator": "",
532
+ "aria-hidden": "true",
533
+ style: { color: "var(--iris-muted)", display: "inline-flex", alignItems: "center" },
534
+ children: separator
535
+ },
536
+ `sep-${i}`
537
+ ) : null;
538
+ return /* @__PURE__ */ jsxRuntime.jsxs(React13__namespace.Fragment, { children: [
539
+ itemNode,
540
+ sepNode
541
+ ] }, `pair-${i}`);
542
+ })
543
+ }
544
+ )
545
+ }
546
+ );
547
+ }
548
+ );
549
+ var IrisBreadcrumbItem = React13__namespace.forwardRef(
550
+ function IrisBreadcrumbItem2({ href, isCurrent = false, style, children, ...rest }, ref) {
551
+ const baseStyle = {
552
+ color: isCurrent ? "var(--iris-muted)" : "var(--iris-primary)",
553
+ textDecoration: "none",
554
+ cursor: isCurrent ? "default" : href ? "pointer" : "default"
555
+ };
556
+ const common = {
557
+ ...rest,
558
+ "aria-current": isCurrent ? "page" : void 0,
559
+ "data-iris-breadcrumb-crumb": "",
560
+ style: { ...baseStyle, ...style }
561
+ };
562
+ if (href && !isCurrent) {
563
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { ...common, ref, href, children });
564
+ }
565
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { ...common, ref, children });
566
+ }
567
+ );
568
+ function IrisAdminBreadcrumb({
569
+ trail,
570
+ showIcon = true,
571
+ hideSingle = false,
572
+ separator = "/",
573
+ onSelect
574
+ }) {
575
+ if (trail.length === 0 || hideSingle && trail.length === 1) return null;
576
+ return /* @__PURE__ */ jsxRuntime.jsx(IrisBreadcrumb, { separator, children: trail.map((node, i) => {
577
+ const last = i === trail.length - 1;
578
+ return /* @__PURE__ */ jsxRuntime.jsxs(
579
+ IrisBreadcrumbItem,
580
+ {
581
+ "data-iris-admin-crumb": "",
582
+ style: {
583
+ display: "inline-flex",
584
+ alignItems: "center",
585
+ cursor: last ? "default" : "pointer"
586
+ },
587
+ onClick: last ? void 0 : () => onSelect?.(node.key, node),
588
+ children: [
589
+ showIcon && node.icon ? /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: node.icon, size: 14, style: { marginInlineEnd: 4 } }) : null,
590
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.title })
591
+ ]
592
+ },
593
+ node.key
594
+ );
595
+ }) });
596
+ }
597
+
598
+ // src/admin/useTabsNav.ts
599
+ function useTabsNav(nav) {
600
+ const state = useStore(nav.store);
601
+ return {
602
+ tabs: state.tabs,
603
+ activeKey: state.activeKey,
604
+ cacheKeys: state.tabs.map((t) => nav.cacheKey(t.key)),
605
+ open: nav.open,
606
+ activate: nav.activate,
607
+ close: nav.close,
608
+ closeOthers: nav.closeOthers,
609
+ closeAll: nav.closeAll,
610
+ closeLeft: nav.closeLeft,
611
+ closeRight: nav.closeRight,
612
+ refresh: nav.refresh,
613
+ setPinned: nav.setPinned,
614
+ cacheKey: nav.cacheKey
615
+ };
616
+ }
617
+ function useMachine(factory) {
618
+ const machine = React13.useMemo(factory, []);
619
+ const state = useStore(machine.store);
620
+ return [state, machine.send];
621
+ }
622
+ var DropdownContext = React13__namespace.createContext(null);
623
+ function useDropdownContext(componentName) {
624
+ const ctx = React13__namespace.useContext(DropdownContext);
625
+ if (!ctx) {
626
+ throw new Error(`[iris-ui] ${componentName} must be a descendant of <IrisDropdown>`);
627
+ }
628
+ return ctx;
629
+ }
630
+ function IrisDropdown({
631
+ open: openProp,
632
+ defaultOpen = false,
633
+ onOpenChange,
634
+ placement = "bottom-start",
635
+ offset = 6,
636
+ children
637
+ }) {
638
+ const isControlled = openProp !== void 0;
639
+ const [state, send] = useMachine(() => core.createFloatingMachine(defaultOpen ? "open" : "closed"));
640
+ const internalOpen = state.value === "open";
641
+ React13__namespace.useEffect(() => {
642
+ if (!isControlled) return;
643
+ const target = openProp ? "open" : "closed";
644
+ if (state.value !== target) {
645
+ send({ type: openProp ? "OPEN" : "CLOSE" });
646
+ }
647
+ }, [isControlled, openProp, state.value, send]);
648
+ const open = isControlled ? Boolean(openProp) : internalOpen;
649
+ const setOpen = React13__namespace.useCallback(
650
+ (next) => {
651
+ if (!isControlled) send({ type: next ? "OPEN" : "CLOSE" });
652
+ onOpenChange?.(next);
653
+ },
654
+ [isControlled, send, onOpenChange]
655
+ );
656
+ const triggerRef = React13__namespace.useRef(null);
657
+ const contentRef = React13__namespace.useRef(null);
658
+ const contentId = React13__namespace.useId();
659
+ const value = React13__namespace.useMemo(
660
+ () => ({
661
+ open,
662
+ setOpen,
663
+ triggerRef,
664
+ contentRef,
665
+ contentId,
666
+ placement,
667
+ offset
668
+ }),
669
+ [open, setOpen, contentId, placement, offset]
670
+ );
671
+ return /* @__PURE__ */ jsxRuntime.jsx(DropdownContext.Provider, { value, children });
672
+ }
673
+ function mergeRefs(...refs) {
674
+ return (value) => {
675
+ for (const ref of refs) {
676
+ if (!ref) continue;
677
+ if (typeof ref === "function") ref(value);
678
+ else ref.current = value;
679
+ }
680
+ };
681
+ }
682
+ function isEventHandlerName(key) {
683
+ return /^on[A-Z]/.test(key);
684
+ }
685
+ function mergeSlotProps(slot, child) {
686
+ const merged = { ...slot };
687
+ for (const key of Object.keys(child)) {
688
+ const slotValue = slot[key];
689
+ const childValue = child[key];
690
+ if (key === "style") {
691
+ merged.style = {
692
+ ...slotValue ?? {},
693
+ ...childValue ?? {}
694
+ };
695
+ } else if (key === "className") {
696
+ merged.className = [slotValue, childValue].filter(Boolean).join(" ").trim() || void 0;
697
+ } else if (isEventHandlerName(key) && typeof slotValue === "function" && typeof childValue === "function") {
698
+ merged[key] = core.composeEventHandlers(
699
+ slotValue,
700
+ childValue
701
+ );
702
+ } else if (childValue !== void 0) {
703
+ merged[key] = childValue;
704
+ }
705
+ }
706
+ return merged;
707
+ }
708
+ var IrisSlot = React13__namespace.forwardRef(function IrisSlot2({ children, ...slotProps }, forwardedRef) {
709
+ if (!React13__namespace.isValidElement(children)) return null;
710
+ const child = children;
711
+ const childProps = child.props ?? {};
712
+ const merged = mergeSlotProps(slotProps, childProps);
713
+ const childRef = child.ref ?? null;
714
+ if (forwardedRef || childRef) {
715
+ merged.ref = mergeRefs(
716
+ forwardedRef,
717
+ childRef
718
+ );
719
+ }
720
+ return React13__namespace.cloneElement(child, merged);
721
+ });
722
+ var IrisDropdownTrigger = React13__namespace.forwardRef(
723
+ function IrisDropdownTrigger2({ asChild = false, onClick, onKeyDown, children, ...rest }, forwardedRef) {
724
+ const ctx = useDropdownContext("IrisDropdownTrigger");
725
+ const captureRef = React13__namespace.useCallback(
726
+ (el) => {
727
+ ctx.triggerRef.current = el;
728
+ if (typeof forwardedRef === "function") forwardedRef(el);
729
+ else if (forwardedRef)
730
+ forwardedRef.current = el;
731
+ },
732
+ [ctx, forwardedRef]
733
+ );
734
+ const handleClick = core.composeEventHandlers(
735
+ onClick,
736
+ () => ctx.setOpen(!ctx.open)
737
+ );
738
+ const handleKeyDown = core.composeEventHandlers(
739
+ onKeyDown,
740
+ (e) => {
741
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
742
+ e.preventDefault();
743
+ ctx.setOpen(true);
744
+ }
745
+ }
746
+ );
747
+ const triggerProps = {
748
+ "aria-haspopup": "menu",
749
+ "aria-expanded": ctx.open,
750
+ "aria-controls": ctx.contentId,
751
+ "data-state": ctx.open ? "open" : "closed",
752
+ onClick: handleClick,
753
+ onKeyDown: handleKeyDown
754
+ };
755
+ if (asChild) {
756
+ if (!React13__namespace.isValidElement(children)) {
757
+ if (process.env.NODE_ENV !== "production") {
758
+ console.warn("[iris-ui] IrisDropdownTrigger asChild requires a single React element");
759
+ }
760
+ return null;
761
+ }
762
+ return /* @__PURE__ */ jsxRuntime.jsx(IrisSlot, { ref: captureRef, ...rest, ...triggerProps, children });
763
+ }
764
+ return /* @__PURE__ */ jsxRuntime.jsx(
765
+ "button",
766
+ {
767
+ type: "button",
768
+ ...rest,
769
+ ...triggerProps,
770
+ ref: captureRef,
771
+ children
772
+ }
773
+ );
774
+ }
775
+ );
776
+ function useFloating(options) {
777
+ const {
778
+ anchor,
779
+ floating,
780
+ open,
781
+ placement = "bottom",
782
+ strategy = "absolute",
783
+ offset,
784
+ flip = true,
785
+ shift = true,
786
+ size = false,
787
+ middleware: extraMiddleware,
788
+ arrow
789
+ } = options;
790
+ const [x, setX] = React13__namespace.useState(0);
791
+ const [y, setY] = React13__namespace.useState(0);
792
+ const [finalPlacement, setFinalPlacement] = React13__namespace.useState(placement);
793
+ const [arrowX, setArrowX] = React13__namespace.useState();
794
+ const [arrowY, setArrowY] = React13__namespace.useState();
795
+ const middleware = React13__namespace.useMemo(() => {
796
+ const mw = [];
797
+ if (offset !== void 0) mw.push(dom.offset(offset));
798
+ if (flip) mw.push(dom.flip());
799
+ if (shift) mw.push(dom.shift({ padding: 8 }));
800
+ if (size) {
801
+ const padding = typeof size === "number" ? size : 8;
802
+ mw.push(
803
+ dom.size({
804
+ padding,
805
+ apply({ availableWidth, availableHeight, elements }) {
806
+ Object.assign(elements.floating.style, {
807
+ maxWidth: `${Math.max(0, Math.floor(availableWidth))}px`,
808
+ maxHeight: `${Math.max(0, Math.floor(availableHeight))}px`
809
+ });
810
+ }
811
+ })
812
+ );
813
+ }
814
+ if (arrow?.current) mw.push(dom.arrow({ element: arrow.current }));
815
+ if (extraMiddleware) mw.push(...extraMiddleware);
816
+ return mw;
817
+ }, [offset, flip, shift, size, extraMiddleware, arrow]);
818
+ const middlewareRef = React13__namespace.useRef(middleware);
819
+ middlewareRef.current = middleware;
820
+ const epochRef = React13__namespace.useRef(0);
821
+ const update = React13__namespace.useCallback(async () => {
822
+ const a = anchor.current;
823
+ const f = floating.current;
824
+ if (!a || !f) return;
825
+ const token = ++epochRef.current;
826
+ const result = await dom.computePosition(a, f, {
827
+ placement,
828
+ strategy,
829
+ middleware: middlewareRef.current
830
+ });
831
+ if (token !== epochRef.current) return;
832
+ setX(result.x);
833
+ setY(result.y);
834
+ setFinalPlacement(result.placement);
835
+ const arrowData = result.middlewareData.arrow;
836
+ setArrowX(arrowData?.x);
837
+ setArrowY(arrowData?.y);
838
+ }, [anchor, floating, placement, strategy]);
839
+ React13__namespace.useEffect(() => {
840
+ if (!open) return;
841
+ const a = anchor.current;
842
+ const f = floating.current;
843
+ if (!a || !f) return;
844
+ const cleanup = dom.autoUpdate(a, f, () => {
845
+ void update();
846
+ });
847
+ return () => {
848
+ epochRef.current++;
849
+ cleanup();
850
+ };
851
+ }, [open, anchor, floating, update]);
852
+ const floatingStyles = React13__namespace.useMemo(
853
+ () => ({
854
+ position: strategy,
855
+ top: 0,
856
+ left: 0,
857
+ transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
858
+ width: "max-content"
859
+ }),
860
+ [strategy, x, y]
861
+ );
862
+ const OPPOSITE = {
863
+ top: "bottom",
864
+ bottom: "top",
865
+ left: "right",
866
+ right: "left"
867
+ };
868
+ const arrowSide = OPPOSITE[finalPlacement.split("-")[0]];
869
+ return { x, y, strategy, finalPlacement, floatingStyles, update, arrowX, arrowY, arrowSide };
870
+ }
871
+ function useDismiss(options) {
872
+ const { enabled, escape = true, outsidePointerDown = true } = options;
873
+ const onDismissRef = React13__namespace.useRef(options.onDismiss);
874
+ const excludeRef = React13__namespace.useRef(options.exclude);
875
+ React13__namespace.useEffect(() => {
876
+ onDismissRef.current = options.onDismiss;
877
+ excludeRef.current = options.exclude;
878
+ });
879
+ React13__namespace.useEffect(() => {
880
+ if (!enabled || typeof document === "undefined") return;
881
+ const isExcluded = (target) => {
882
+ if (!target || !(target instanceof Node)) return false;
883
+ for (const r of excludeRef.current) {
884
+ const el = r.current;
885
+ if (el && (el === target || el.contains(target))) return true;
886
+ }
887
+ return false;
888
+ };
889
+ const onPointerDown = (event) => {
890
+ if (isExcluded(event.target)) return;
891
+ onDismissRef.current();
892
+ };
893
+ const onKeyDown = (event) => {
894
+ if (event.key === "Escape") {
895
+ event.stopPropagation();
896
+ onDismissRef.current();
897
+ }
898
+ };
899
+ if (outsidePointerDown) document.addEventListener("pointerdown", onPointerDown, true);
900
+ if (escape) document.addEventListener("keydown", onKeyDown);
901
+ return () => {
902
+ document.removeEventListener("pointerdown", onPointerDown, true);
903
+ document.removeEventListener("keydown", onKeyDown);
904
+ };
905
+ }, [enabled, escape, outsidePointerDown]);
906
+ }
907
+ var IrisDropdownMenu = React13__namespace.forwardRef(
908
+ function IrisDropdownMenu2({ portalTarget, style, onKeyDown, children, ...rest }, forwardedRef) {
909
+ const ctx = useDropdownContext("IrisDropdownMenu");
910
+ const innerRef = React13__namespace.useRef(null);
911
+ const captureRef = React13__namespace.useCallback(
912
+ (el) => {
913
+ innerRef.current = el;
914
+ ctx.contentRef.current = el;
915
+ if (typeof forwardedRef === "function") forwardedRef(el);
916
+ else if (forwardedRef)
917
+ forwardedRef.current = el;
918
+ },
919
+ [ctx, forwardedRef]
920
+ );
921
+ const { floatingStyles } = useFloating({
922
+ anchor: ctx.triggerRef,
923
+ floating: innerRef,
924
+ open: ctx.open,
925
+ placement: ctx.placement,
926
+ offset: ctx.offset
927
+ });
928
+ useDismiss({
929
+ enabled: ctx.open,
930
+ exclude: [ctx.triggerRef, ctx.contentRef],
931
+ onDismiss: () => ctx.setOpen(false)
932
+ });
933
+ const wasOpenRef = React13__namespace.useRef(false);
934
+ React13__namespace.useEffect(() => {
935
+ if (ctx.open && !wasOpenRef.current) {
936
+ queueMicrotask(() => {
937
+ const items = innerRef.current?.querySelectorAll('[role="menuitem"]');
938
+ items?.[0]?.focus();
939
+ });
940
+ } else if (!ctx.open && wasOpenRef.current) {
941
+ ctx.triggerRef.current?.focus?.();
942
+ }
943
+ wasOpenRef.current = ctx.open;
944
+ }, [ctx.open, ctx.triggerRef]);
945
+ const typeaheadRef = React13__namespace.useRef({ buffer: "", timer: null });
946
+ const handleKeyDown = (e) => {
947
+ onKeyDown?.(e);
948
+ if (!ctx.open) return;
949
+ const items = Array.from(
950
+ innerRef.current?.querySelectorAll(
951
+ '[role="menuitem"]:not([aria-disabled="true"])'
952
+ ) ?? []
953
+ );
954
+ if (items.length === 0) return;
955
+ const current = document.activeElement;
956
+ const index = current ? items.indexOf(current) : -1;
957
+ switch (e.key) {
958
+ case "ArrowDown": {
959
+ e.preventDefault();
960
+ const next = index < 0 ? 0 : (index + 1) % items.length;
961
+ items[next]?.focus();
962
+ break;
963
+ }
964
+ case "ArrowUp": {
965
+ e.preventDefault();
966
+ const next = index <= 0 ? items.length - 1 : index - 1;
967
+ items[next]?.focus();
968
+ break;
969
+ }
970
+ case "Home":
971
+ e.preventDefault();
972
+ items[0]?.focus();
973
+ break;
974
+ case "End":
975
+ e.preventDefault();
976
+ items[items.length - 1]?.focus();
977
+ break;
978
+ case "Tab":
979
+ ctx.setOpen(false);
980
+ break;
981
+ default: {
982
+ if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
983
+ const ta = typeaheadRef.current;
984
+ ta.buffer += e.key;
985
+ if (ta.timer) clearTimeout(ta.timer);
986
+ ta.timer = setTimeout(() => {
987
+ ta.buffer = "";
988
+ }, 500);
989
+ const match = core.matchTypeahead(
990
+ items.map((it) => it.textContent ?? ""),
991
+ ta.buffer,
992
+ index
993
+ );
994
+ if (match >= 0) {
995
+ e.preventDefault();
996
+ items[match]?.focus();
997
+ }
998
+ }
999
+ }
1000
+ }
1001
+ };
1002
+ if (!ctx.open) return null;
1003
+ const node = /* @__PURE__ */ jsxRuntime.jsx(
1004
+ "div",
1005
+ {
1006
+ ...rest,
1007
+ ref: captureRef,
1008
+ id: ctx.contentId,
1009
+ role: "menu",
1010
+ tabIndex: -1,
1011
+ "data-iris-dropdown-menu": "",
1012
+ "data-state": "open",
1013
+ onKeyDown: handleKeyDown,
1014
+ style: {
1015
+ ...floatingStyles,
1016
+ background: "var(--iris-surface)",
1017
+ color: "var(--iris-foreground)",
1018
+ border: "1px solid var(--iris-border)",
1019
+ borderRadius: "var(--iris-radius-md, 6px)",
1020
+ padding: "var(--iris-padding-sm, 4px)",
1021
+ boxShadow: "0 8px 24px -8px rgba(0, 0, 0, 0.16), 0 4px 8px -2px rgba(0, 0, 0, 0.08)",
1022
+ minWidth: 160,
1023
+ outline: "none",
1024
+ zIndex: 1e3,
1025
+ ...style
1026
+ },
1027
+ children
1028
+ }
1029
+ );
1030
+ if (portalTarget === false) return node;
1031
+ if (typeof document === "undefined") return null;
1032
+ return reactDom.createPortal(node, portalTarget ?? document.body);
1033
+ }
1034
+ );
1035
+ var IrisDropdownItem = React13__namespace.forwardRef(
1036
+ function IrisDropdownItem2({ disabled = false, keepOpen = false, onSelect, onClick, onKeyDown, style, children, ...rest }, ref) {
1037
+ const ctx = useDropdownContext("IrisDropdownItem");
1038
+ const [hovered, setHovered] = React13__namespace.useState(false);
1039
+ const fire = (event) => {
1040
+ if (disabled) return;
1041
+ onSelect?.(event);
1042
+ if (!keepOpen) ctx.setOpen(false);
1043
+ };
1044
+ const handleClick = core.composeEventHandlers(
1045
+ onClick,
1046
+ (e) => fire(e)
1047
+ );
1048
+ const handleKeyDown = core.composeEventHandlers(
1049
+ onKeyDown,
1050
+ (e) => {
1051
+ if (e.key === "Enter" || e.key === " ") {
1052
+ e.preventDefault();
1053
+ fire(e);
1054
+ }
1055
+ }
1056
+ );
1057
+ return /* @__PURE__ */ jsxRuntime.jsx(
1058
+ "div",
1059
+ {
1060
+ ...rest,
1061
+ ref,
1062
+ role: "menuitem",
1063
+ tabIndex: disabled ? -1 : 0,
1064
+ "aria-disabled": disabled ? "true" : void 0,
1065
+ "data-iris-dropdown-item": "",
1066
+ "data-disabled": disabled ? "" : void 0,
1067
+ onClick: handleClick,
1068
+ onKeyDown: handleKeyDown,
1069
+ onPointerEnter: () => setHovered(true),
1070
+ onPointerLeave: () => setHovered(false),
1071
+ onFocus: () => setHovered(true),
1072
+ onBlur: () => setHovered(false),
1073
+ style: {
1074
+ display: "flex",
1075
+ alignItems: "center",
1076
+ gap: "var(--iris-gap-sm, 6px)",
1077
+ padding: "6px var(--iris-padding-md, 12px)",
1078
+ borderRadius: "var(--iris-radius-sm, 4px)",
1079
+ cursor: disabled ? "not-allowed" : "pointer",
1080
+ opacity: disabled ? 0.5 : 1,
1081
+ background: hovered && !disabled ? "var(--iris-surface-hover)" : "transparent",
1082
+ color: "inherit",
1083
+ outline: "none",
1084
+ fontSize: 14,
1085
+ transition: "background-color 80ms ease",
1086
+ ...style
1087
+ },
1088
+ children
1089
+ }
1090
+ );
1091
+ }
1092
+ );
1093
+ var IrisDropdownSeparator = React13__namespace.forwardRef(
1094
+ function IrisDropdownSeparator2({ style, ...rest }, ref) {
1095
+ return /* @__PURE__ */ jsxRuntime.jsx(
1096
+ "div",
1097
+ {
1098
+ ...rest,
1099
+ ref,
1100
+ role: "separator",
1101
+ "data-iris-dropdown-separator": "",
1102
+ style: {
1103
+ height: 1,
1104
+ background: "var(--iris-border)",
1105
+ margin: "4px 0",
1106
+ ...style
1107
+ }
1108
+ }
1109
+ );
1110
+ }
1111
+ );
1112
+ function IrisAdminTabs({
1113
+ nav,
1114
+ onChange,
1115
+ onClose,
1116
+ onRefresh
1117
+ }) {
1118
+ const t = useTabsNav(nav);
1119
+ const { t: translate } = useI18n();
1120
+ const tablistRef = React13__namespace.useRef(null);
1121
+ const pendingFocus = React13__namespace.useRef(null);
1122
+ React13__namespace.useEffect(() => {
1123
+ if (!pendingFocus.current) return;
1124
+ const key = pendingFocus.current;
1125
+ pendingFocus.current = null;
1126
+ tablistRef.current?.querySelectorAll("[data-iris-tab-label]").forEach((el) => {
1127
+ if (el.getAttribute("data-key") === key) el.focus();
1128
+ });
1129
+ });
1130
+ const activate = (key) => {
1131
+ nav.activate(key);
1132
+ onChange?.(key);
1133
+ };
1134
+ const close = (key) => {
1135
+ nav.close(key);
1136
+ onClose?.(key);
1137
+ };
1138
+ const refresh = (key) => {
1139
+ nav.refresh(key);
1140
+ onRefresh?.(key);
1141
+ };
1142
+ const onKeyDown = (e) => {
1143
+ const tabs = t.tabs;
1144
+ const idx = tabs.findIndex((x) => x.key === t.activeKey);
1145
+ if (idx < 0 || tabs.length === 0) return;
1146
+ if (e.key === "Delete" || e.key === "Backspace") {
1147
+ const cur = tabs[idx];
1148
+ if (cur && core.isClosable(cur)) {
1149
+ e.preventDefault();
1150
+ close(cur.key);
1151
+ pendingFocus.current = nav.getState().activeKey ?? null;
1152
+ }
1153
+ return;
1154
+ }
1155
+ let target = -1;
1156
+ if (e.key === "ArrowRight") target = (idx + 1) % tabs.length;
1157
+ else if (e.key === "ArrowLeft") target = (idx - 1 + tabs.length) % tabs.length;
1158
+ else if (e.key === "Home") target = 0;
1159
+ else if (e.key === "End") target = tabs.length - 1;
1160
+ else return;
1161
+ e.preventDefault();
1162
+ const key = tabs[target]?.key;
1163
+ if (!key) return;
1164
+ activate(key);
1165
+ pendingFocus.current = key;
1166
+ };
1167
+ const chip = (tab) => {
1168
+ const active = tab.key === t.activeKey;
1169
+ const closable = core.isClosable(tab);
1170
+ const label = /* @__PURE__ */ jsxRuntime.jsxs(
1171
+ "button",
1172
+ {
1173
+ type: "button",
1174
+ role: "tab",
1175
+ "data-iris-tab-label": "",
1176
+ "data-key": tab.key,
1177
+ "aria-selected": active ? "true" : "false",
1178
+ "aria-current": active ? "page" : void 0,
1179
+ tabIndex: active ? 0 : -1,
1180
+ style: {
1181
+ display: "inline-flex",
1182
+ alignItems: "center",
1183
+ gap: 6,
1184
+ border: "none",
1185
+ background: "transparent",
1186
+ color: "inherit",
1187
+ font: "inherit",
1188
+ fontSize: 13,
1189
+ cursor: "pointer",
1190
+ padding: 0
1191
+ },
1192
+ onClick: () => activate(tab.key),
1193
+ children: [
1194
+ tab.icon ? /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: tab.icon, size: 14 }) : null,
1195
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { whiteSpace: "nowrap" }, children: tab.title })
1196
+ ]
1197
+ }
1198
+ );
1199
+ const closeBtn = closable ? /* @__PURE__ */ jsxRuntime.jsx(
1200
+ "button",
1201
+ {
1202
+ type: "button",
1203
+ tabIndex: -1,
1204
+ "data-iris-tab-close": "",
1205
+ "aria-label": translate("admin.closeTab", { title: tab.title }),
1206
+ style: {
1207
+ display: "inline-flex",
1208
+ alignItems: "center",
1209
+ justifyContent: "center",
1210
+ width: 16,
1211
+ height: 16,
1212
+ border: "none",
1213
+ borderRadius: 4,
1214
+ background: "transparent",
1215
+ color: "inherit",
1216
+ cursor: "pointer",
1217
+ padding: 0,
1218
+ opacity: 0.6
1219
+ },
1220
+ onClick: (e) => {
1221
+ e.stopPropagation();
1222
+ close(tab.key);
1223
+ },
1224
+ children: /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: "x", size: 12 })
1225
+ }
1226
+ ) : tab.pinned ? /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: "check-circle", size: 12, style: { opacity: 0.5 } }) : null;
1227
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1228
+ "div",
1229
+ {
1230
+ "data-iris-tab": "",
1231
+ "data-active": active ? "true" : void 0,
1232
+ style: {
1233
+ display: "inline-flex",
1234
+ alignItems: "center",
1235
+ gap: 4,
1236
+ padding: "5px 8px 5px 10px",
1237
+ borderRadius: "var(--iris-radius-md, 6px)",
1238
+ border: `1px solid ${active ? "var(--iris-primary)" : "var(--iris-border)"}`,
1239
+ background: active ? "var(--iris-primary)" : "var(--iris-surface)",
1240
+ color: active ? "var(--iris-primary-foreground, #fff)" : "var(--iris-foreground)",
1241
+ flex: "0 0 auto"
1242
+ },
1243
+ children: [
1244
+ label,
1245
+ closeBtn
1246
+ ]
1247
+ },
1248
+ tab.key
1249
+ );
1250
+ };
1251
+ const activeKey = t.activeKey;
1252
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1253
+ "div",
1254
+ {
1255
+ "data-iris-admin-tabs": "",
1256
+ style: {
1257
+ display: "flex",
1258
+ alignItems: "center",
1259
+ gap: 6,
1260
+ padding: "6px 10px",
1261
+ borderBottom: "1px solid var(--iris-border)",
1262
+ background: "var(--iris-background)"
1263
+ },
1264
+ children: [
1265
+ /* @__PURE__ */ jsxRuntime.jsx(
1266
+ "div",
1267
+ {
1268
+ ref: tablistRef,
1269
+ "data-iris-tabs-scroll": "",
1270
+ role: "tablist",
1271
+ "aria-label": translate("admin.openPages"),
1272
+ onKeyDown,
1273
+ style: { display: "flex", alignItems: "center", gap: 6, overflowX: "auto", flex: 1 },
1274
+ children: t.tabs.map((tab) => chip(tab))
1275
+ }
1276
+ ),
1277
+ /* @__PURE__ */ jsxRuntime.jsxs(IrisDropdown, { children: [
1278
+ /* @__PURE__ */ jsxRuntime.jsx(
1279
+ IrisDropdownTrigger,
1280
+ {
1281
+ "aria-label": translate("admin.tabActions"),
1282
+ style: {
1283
+ display: "inline-flex",
1284
+ alignItems: "center",
1285
+ justifyContent: "center",
1286
+ width: 28,
1287
+ height: 28,
1288
+ borderRadius: "var(--iris-radius-md, 6px)",
1289
+ border: "1px solid var(--iris-border)",
1290
+ background: "var(--iris-surface)",
1291
+ color: "var(--iris-foreground)",
1292
+ cursor: "pointer"
1293
+ },
1294
+ children: /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: "more-horizontal", size: 16 })
1295
+ }
1296
+ ),
1297
+ /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownMenu, { children: activeKey ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1298
+ /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => refresh(activeKey), children: translate("admin.refresh") }),
1299
+ /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => close(activeKey), children: translate("admin.close") }),
1300
+ /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownSeparator, {}),
1301
+ /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => nav.closeOthers(activeKey), children: translate("admin.closeOthers") }),
1302
+ /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => nav.closeAll(), children: translate("admin.closeAll") })
1303
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { disabled: true, children: "No active tab" }) })
1304
+ ] })
1305
+ ]
1306
+ }
1307
+ );
1308
+ }
1309
+ function useAdminShell(config) {
1310
+ const { menus, activeKey, defaultActiveKey, onActiveKeyChange, onSelect, tabs } = config;
1311
+ const activeControlled = activeKey !== void 0;
1312
+ const latest = React13__namespace.useRef({ onActiveKeyChange, onSelect });
1313
+ latest.current = { onActiveKeyChange, onSelect };
1314
+ const shellRef = React13__namespace.useRef(null);
1315
+ if (shellRef.current === null) {
1316
+ shellRef.current = core.createAdminShell({
1317
+ menus,
1318
+ defaultActiveKey: activeKey ?? defaultActiveKey,
1319
+ tabs,
1320
+ onActiveChange: (key, node) => {
1321
+ latest.current.onActiveKeyChange?.(key);
1322
+ if (node) latest.current.onSelect?.(key, node);
1323
+ }
1324
+ });
1325
+ }
1326
+ const shell = shellRef.current;
1327
+ const state = useStore(shell.store);
1328
+ React13__namespace.useEffect(() => {
1329
+ shell.setMenus(menus);
1330
+ }, [shell, menus]);
1331
+ React13__namespace.useEffect(() => {
1332
+ if (activeControlled && shell.getActiveKey() !== (activeKey ?? null)) {
1333
+ shell.store.setState({ activeKey: activeKey ?? null });
1334
+ }
1335
+ });
1336
+ const current = activeControlled ? activeKey ?? "" : state.activeKey ?? "";
1337
+ return {
1338
+ activeKey: current,
1339
+ navigate: shell.navigate,
1340
+ syncFromTab: shell.syncFromTab,
1341
+ breadcrumb: current ? core.findNavPath(menus, current) : []
1342
+ };
1343
+ }
1344
+ function TabsSync({
1345
+ nav,
1346
+ activeKey,
1347
+ onActivate
1348
+ }) {
1349
+ const state = useStore(nav.store);
1350
+ React13__namespace.useEffect(() => {
1351
+ if (state.activeKey && state.activeKey !== activeKey) onActivate(state.activeKey);
1352
+ }, [state.activeKey, activeKey, onActivate]);
1353
+ return null;
1354
+ }
1355
+ function IrisAdminLayout({
1356
+ menus,
1357
+ activeKey,
1358
+ defaultActiveKey,
1359
+ onActiveKeyChange,
1360
+ collapsed,
1361
+ defaultCollapsed = false,
1362
+ onCollapsedChange,
1363
+ mode = "sidebar",
1364
+ appTitle = "Iris Admin",
1365
+ tabs,
1366
+ showBreadcrumb = true,
1367
+ sidebarWidth = 240,
1368
+ collapsedWidth = 64,
1369
+ logo,
1370
+ toolbar,
1371
+ footer,
1372
+ onSelect,
1373
+ children
1374
+ }) {
1375
+ const { t } = useI18n();
1376
+ const {
1377
+ activeKey: currentActive,
1378
+ navigate,
1379
+ syncFromTab,
1380
+ breadcrumb: trail
1381
+ } = useAdminShell({ menus, activeKey, defaultActiveKey, onActiveKeyChange, onSelect, tabs });
1382
+ const collapseControlled = collapsed !== void 0;
1383
+ const [internalCollapsed, setInternalCollapsed] = React13__namespace.useState(defaultCollapsed);
1384
+ const currentCollapsed = collapseControlled ? collapsed : internalCollapsed;
1385
+ const setCollapsed = React13__namespace.useCallback(
1386
+ (next) => {
1387
+ if (!collapseControlled) setInternalCollapsed(next);
1388
+ onCollapsedChange?.(next);
1389
+ },
1390
+ [collapseControlled, onCollapsedChange]
1391
+ );
1392
+ const handleSelect = (_key, node) => navigate(node);
1393
+ const renderContent = () => typeof children === "function" ? children({ activeKey: currentActive }) : children;
1394
+ if (mode === "full-content") {
1395
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-iris-admin-layout": "", "data-mode": "full-content", style: { height: "100%" }, children: renderContent() });
1396
+ }
1397
+ const defaultLogo = (state) => /* @__PURE__ */ jsxRuntime.jsxs(
1398
+ "div",
1399
+ {
1400
+ "data-iris-admin-logo": "",
1401
+ style: {
1402
+ display: "flex",
1403
+ alignItems: "center",
1404
+ gap: 10,
1405
+ height: 52,
1406
+ padding: "0 16px",
1407
+ color: "var(--iris-foreground)",
1408
+ fontWeight: 700,
1409
+ fontSize: 16,
1410
+ whiteSpace: "nowrap",
1411
+ overflow: "hidden",
1412
+ flexShrink: 0
1413
+ },
1414
+ children: [
1415
+ /* @__PURE__ */ jsxRuntime.jsx(
1416
+ "span",
1417
+ {
1418
+ style: {
1419
+ display: "inline-flex",
1420
+ alignItems: "center",
1421
+ justifyContent: "center",
1422
+ width: 28,
1423
+ height: 28,
1424
+ borderRadius: "var(--iris-radius-md, 6px)",
1425
+ background: "var(--iris-primary)",
1426
+ color: "var(--iris-primary-foreground, #fff)",
1427
+ flexShrink: 0
1428
+ },
1429
+ children: /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: "menu", size: 18 })
1430
+ }
1431
+ ),
1432
+ state.collapsed ? null : /* @__PURE__ */ jsxRuntime.jsx("span", { children: appTitle })
1433
+ ]
1434
+ }
1435
+ );
1436
+ const renderLogo = (state) => typeof logo === "function" ? logo(state) : logo ?? defaultLogo(state);
1437
+ const collapseToggle = /* @__PURE__ */ jsxRuntime.jsx(
1438
+ "button",
1439
+ {
1440
+ type: "button",
1441
+ "data-iris-admin-collapse": "",
1442
+ "aria-label": currentCollapsed ? t("admin.expandSidebar") : t("admin.collapseSidebar"),
1443
+ "aria-pressed": currentCollapsed ? "true" : "false",
1444
+ style: {
1445
+ display: "inline-flex",
1446
+ alignItems: "center",
1447
+ justifyContent: "center",
1448
+ width: 32,
1449
+ height: 32,
1450
+ border: "none",
1451
+ borderRadius: "var(--iris-radius-md, 6px)",
1452
+ background: "transparent",
1453
+ color: "var(--iris-foreground)",
1454
+ cursor: "pointer",
1455
+ flexShrink: 0
1456
+ },
1457
+ onClick: () => setCollapsed(!currentCollapsed),
1458
+ children: /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: currentCollapsed ? "chevron-right" : "menu", size: 18 })
1459
+ }
1460
+ );
1461
+ const headerBar = /* @__PURE__ */ jsxRuntime.jsxs(
1462
+ "div",
1463
+ {
1464
+ "data-iris-admin-headerbar": "",
1465
+ style: {
1466
+ display: "flex",
1467
+ alignItems: "center",
1468
+ gap: 12,
1469
+ height: 52,
1470
+ padding: "0 16px",
1471
+ borderBottom: "1px solid var(--iris-border)",
1472
+ background: "var(--iris-background)"
1473
+ },
1474
+ children: [
1475
+ collapseToggle,
1476
+ showBreadcrumb ? /* @__PURE__ */ jsxRuntime.jsx(IrisAdminBreadcrumb, { trail, onSelect: handleSelect }) : null,
1477
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1 } }),
1478
+ toolbar ? /* @__PURE__ */ jsxRuntime.jsx("div", { "data-iris-admin-toolbar": "", style: { display: "flex", alignItems: "center", gap: 8 }, children: toolbar }) : null
1479
+ ]
1480
+ }
1481
+ );
1482
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1483
+ IrisSidebarLayout,
1484
+ {
1485
+ "data-iris-admin-layout": "",
1486
+ "data-mode": "sidebar",
1487
+ collapsed: currentCollapsed,
1488
+ onCollapsedChange: setCollapsed,
1489
+ width: sidebarWidth,
1490
+ collapsedWidth,
1491
+ style: { height: "100vh" },
1492
+ sidebar: (state) => /* @__PURE__ */ jsxRuntime.jsxs(
1493
+ "div",
1494
+ {
1495
+ "data-iris-admin-sidebar": "",
1496
+ style: {
1497
+ display: "flex",
1498
+ flexDirection: "column",
1499
+ height: "100%",
1500
+ borderInlineEnd: "1px solid var(--iris-border)",
1501
+ background: "var(--iris-surface)"
1502
+ },
1503
+ children: [
1504
+ renderLogo(state),
1505
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflowY: "auto", overflowX: "hidden", padding: 8 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1506
+ IrisNavMenu,
1507
+ {
1508
+ items: menus,
1509
+ activeKey: currentActive,
1510
+ collapsed: state.collapsed,
1511
+ onSelect: handleSelect
1512
+ }
1513
+ ) })
1514
+ ]
1515
+ }
1516
+ ),
1517
+ children: [
1518
+ tabs ? /* @__PURE__ */ jsxRuntime.jsx(TabsSync, { nav: tabs, activeKey: currentActive, onActivate: syncFromTab }) : null,
1519
+ /* @__PURE__ */ jsxRuntime.jsx(
1520
+ IrisHeaderLayout,
1521
+ {
1522
+ sticky: true,
1523
+ header: /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-iris-admin-header": "", children: [
1524
+ headerBar,
1525
+ tabs ? /* @__PURE__ */ jsxRuntime.jsx(IrisAdminTabs, { nav: tabs }) : null
1526
+ ] }),
1527
+ footer,
1528
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-iris-admin-content": "", style: { padding: 16 }, children: renderContent() })
1529
+ }
1530
+ )
1531
+ ]
1532
+ }
1533
+ );
1534
+ }
1535
+
1536
+ Object.defineProperty(exports, "createTabsNav", {
1537
+ enumerable: true,
1538
+ get: function () { return core.createTabsNav; }
1539
+ });
1540
+ Object.defineProperty(exports, "filterNavByAccess", {
1541
+ enumerable: true,
1542
+ get: function () { return core.filterNavByAccess; }
1543
+ });
1544
+ Object.defineProperty(exports, "findNavNode", {
1545
+ enumerable: true,
1546
+ get: function () { return core.findNavNode; }
1547
+ });
1548
+ Object.defineProperty(exports, "findNavPath", {
1549
+ enumerable: true,
1550
+ get: function () { return core.findNavPath; }
1551
+ });
1552
+ Object.defineProperty(exports, "firstLeaf", {
1553
+ enumerable: true,
1554
+ get: function () { return core.firstLeaf; }
1555
+ });
1556
+ Object.defineProperty(exports, "flattenNav", {
1557
+ enumerable: true,
1558
+ get: function () { return core.flattenNav; }
1559
+ });
1560
+ Object.defineProperty(exports, "isBranch", {
1561
+ enumerable: true,
1562
+ get: function () { return core.isBranch; }
1563
+ });
1564
+ Object.defineProperty(exports, "isClosable", {
1565
+ enumerable: true,
1566
+ get: function () { return core.isClosable; }
1567
+ });
1568
+ Object.defineProperty(exports, "nodeAllowsRoles", {
1569
+ enumerable: true,
1570
+ get: function () { return core.nodeAllowsRoles; }
1571
+ });
1572
+ Object.defineProperty(exports, "visibleNav", {
1573
+ enumerable: true,
1574
+ get: function () { return core.visibleNav; }
1575
+ });
1576
+ exports.IrisAdminBreadcrumb = IrisAdminBreadcrumb;
1577
+ exports.IrisAdminLayout = IrisAdminLayout;
1578
+ exports.IrisAdminTabs = IrisAdminTabs;
1579
+ exports.IrisNavMenu = IrisNavMenu;
1580
+ exports.useAdminShell = useAdminShell;
1581
+ exports.useTabsNav = useTabsNav;
1582
+ //# sourceMappingURL=admin.cjs.map
1583
+ //# sourceMappingURL=admin.cjs.map