@ziyarago/ui 0.16.0 → 0.18.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.
@@ -59,6 +59,33 @@ const fmt = {
59
59
  sar: n => (n == null || Number.isNaN(n)) ? '—' : 'SAR\u00a0' + String(Math.round(n)).replace(/\B(?=(\d{3})+(?!\d))/g, '\u2009'),
60
60
  dur: (s, u) => { if (s == null || Number.isNaN(+s)) return '—'; s = Math.max(0, Math.round(+s)); const m = Math.floor(s / 60), r = s % 60; return `${m} ${(u && u.m) || 'm'} ${String(r).padStart(2, '0')} ${(u && u.s) || 's'}`; },
61
61
  };
62
+ // TopNav overflow: items that do not fit move into a trailing "More" menu. Measured in the browser after render (ResizeObserver on the header);
63
+ // item widths are cached from the render where every item is visible, so hidden items need no measuring. SSR renders every item.
64
+ const tn = new WeakMap();
65
+ const tnMeasure = (el, ctx) => {
66
+ if (!el || !ctx.collapse || typeof ResizeObserver === 'undefined') return;
67
+ let r = tn.get(el);
68
+ if (!r) { r = { w: {}, sig: '' }; r.ro = new ResizeObserver(() => r.run()); tn.set(el, r); if (el.parentElement) r.ro.observe(el.parentElement); }
69
+ r.ctx = ctx;
70
+ r.run = () => {
71
+ const { items, more, set, state } = r.ctx, kids = [...el.children], header = el.parentElement;
72
+ if (!header || !items.length) return;
73
+ const sig = items.map(i => i.value).join('|');
74
+ if (sig !== r.sig) { r.sig = sig; r.w = {}; }
75
+ const n = items.length, shownN = kids.length - (more ? 1 : 0);
76
+ for (let i = 0; i < shownN; i++) r.w[items[i].value] = kids[i].offsetWidth;
77
+ if (more) r.w.__more = kids[kids.length - 1].offsetWidth;
78
+ if (items.some(i => r.w[i.value] == null)) { if (state.fit != null) set({ fit: null }); return; }
79
+ const hs = getComputedStyle(header), es = getComputedStyle(el), bar = header.querySelector('.zg-topnav__brand'), act = header.querySelector('.zg-topnav__actions');
80
+ const gap = parseFloat(hs.columnGap) || 0, ig = parseFloat(es.columnGap) || 0;
81
+ const avail = header.clientWidth - parseFloat(hs.paddingLeft) - parseFloat(hs.paddingRight) - (bar ? bar.offsetWidth : 0) - (act ? act.offsetWidth : 0) - 2 * gap - parseFloat(es.paddingLeft) - parseFloat(es.paddingRight);
82
+ const total = items.reduce((a, i) => a + r.w[i.value], 0) + ig * (n - 1);
83
+ let want = n;
84
+ if (total > avail) { let acc = r.w.__more || 56, k = 0; for (const i of items) { const need = r.w[i.value] + ig; if (acc + need <= avail) { acc += need; k++; } else break; } want = k; }
85
+ if (want !== (state.fit == null ? n : state.fit)) set({ fit: want >= n ? null : want });
86
+ };
87
+ r.run();
88
+ };
62
89
  const nav = (h, it, cur, onChange, collapsed) => h('button', { key: it.value, class: 'zg-navitem', 'aria-current': it.value === cur ? 'page' : undefined, title: collapsed ? it.label : undefined, onClick: () => onChange && onChange(it.value) },
63
90
  it.icon ? icon(h, it.icon, 18) : null, h('span', null, it.label), it.count ? h('span', { class: 'zg-navitem__count' }, it.count) : null);
64
91
 
@@ -116,7 +143,29 @@ export const components = {
116
143
  return h('div', { class: 'zg-table-wrap', tabIndex: 0 }, h('table', { class: 'zg-table' }, h('thead', null, h('tr', null, p.onSelect ? h('th', { style: { width: '44px' } }, h('button', { type: 'button', role: 'checkbox', class: 'zg-check', 'aria-checked': String(!!all), 'aria-label': (p.labels && p.labels.selectAll) || t('DataTable', 'selectAll'), onClick: () => p.onSelect(all ? [] : rows.map(r => r.id)) }, h('span', { class: 'zg-check__box' }, all ? icon(h, 'check', 14) : null))) : null, ...cols.map(th))),
117
144
  h('tbody', null, ...(rows.length ? rows.map((r, ri) => h('tr', { key: r.id, 'aria-selected': sel.includes(r.id) ? 'true' : undefined, class: p.onRowClick ? 'zg-tr--click' : undefined, onClick: p.onRowClick ? () => p.onRowClick(r, ri) : undefined }, p.onSelect ? h('td', null, h('button', { type: 'button', role: 'checkbox', class: 'zg-check', 'aria-checked': String(sel.includes(r.id)), 'aria-label': (p.labels && p.labels.selectRow) || t('DataTable', 'selectRow'), onClick: () => p.onSelect(sel.includes(r.id) ? sel.filter(i => i !== r.id) : [...sel, r.id]) }, h('span', { class: 'zg-check__box' }, sel.includes(r.id) ? icon(h, 'check', 14) : null))) : null, ...cols.map(c => h('td', { key: c.key, class: c.num ? 'num' : undefined, style: cs(c) }, c.render ? c.render(r[c.key], r, h) : (r[c.key] ?? '—'))))) : [h('tr', { key: 'e' }, h('td', { colSpan: cols.length + 1 }, h('div', { class: 'zg-empty' }, slots.empty || p.empty || '—')))])))); } },
118
145
  Timeline: { render: ({ h, props: p }) => h('div', { class: 'zg-timeline' }, ...(p.items || []).map((it, i, a) => h('div', { key: i, class: 'zg-tl' }, h('div', { class: 'zg-tl__rail' }, h('span', { class: 'zg-tl__dot' }), i < a.length - 1 ? h('span', { class: 'zg-tl__line' }) : null), h('div', { class: 'zg-tl__body' }, h('b', null, it.title), it.text ? h('span', null, it.text) : null, h('span', { class: 'zg-tl__time' }, it.time))))) },
119
- TopNav: { render: ({ h, props: p, slots }) => h('header', { class: 'zg-topnav' }, h('div', { class: 'zg-topnav__brand' }, p.brand), h('nav', { class: 'zg-topnav__items', 'aria-label': t('TopNav', 'label') }, ...(p.items || []).map(it => nav(h, it, p.value, p.onChange))), h('div', { class: 'zg-topnav__actions' }, slots.actions || p.actions || null)) },
146
+ TopNav: { state: { open: null, fit: null }, render: ({ h, props: p, slots, state, set }) => {
147
+ const items = p.items || [], collapse = p.overflow !== 'none';
148
+ const fit = collapse && state.fit != null && state.fit < items.length ? state.fit : items.length;
149
+ const shown = items.slice(0, fit), rest = items.slice(fit);
150
+ const more = rest.length ? { value: '__more', label: p.moreLabel || t('TopNav', 'more'), icon: 'ellipsis', children: rest.flatMap(it => it.children && it.children.length ? [{ heading: it.label }, ...it.children] : [it]) } : null;
151
+ const close = () => set({ open: null });
152
+ const entry = it => {
153
+ const kids = it.children;
154
+ if (!kids || !kids.length) return nav(h, it, p.value, p.onChange);
155
+ const open = state.open === it.value, active = it.value !== '__more' ? kids.some(c => c.value === p.value) : rest.some(r => r.value === p.value || (r.children || []).some(c => c.value === p.value));
156
+ const flat = kids.map(c => ({ value: c.value, disabled: c.separator || c.heading || c.disabled }));
157
+ return h('div', { key: it.value, class: 'zg-dd zg-topnav__dd' },
158
+ h('button', { type: 'button', class: 'zg-navitem', 'aria-haspopup': 'menu', 'aria-expanded': String(open), 'aria-current': active ? 'page' : undefined, onClick: () => set({ open: open ? null : it.value }), onKeydown: openKeys(open, () => set({ open: it.value })) },
159
+ it.icon ? icon(h, it.icon, 18) : null, it.value === '__more' ? h('span', { class: 'zg-sr' }, it.label) : h('span', null, it.label), it.count ? h('span', { class: 'zg-navitem__count' }, it.count) : null, it.value === '__more' ? null : icon(h, 'chevron-down', 14)),
160
+ open ? h('div', { class: 'zg-menu', role: 'menu', 'data-zg-dd': '', ref: ddRef(close, { focus: true }), onKeydown: listKeys('[role=menuitem]', { wrap: true, onTab: e => { const tr = trigger(e.currentTarget); tr && tr.focus(); close(); } }) },
161
+ ...kids.map((c, i) => c.separator ? h('div', { key: 's' + i, class: 'zg-menu__sep', role: 'separator' }) : c.heading ? h('div', { key: 'h' + i, class: 'zg-menu__h', role: 'presentation' }, c.heading)
162
+ : h('button', { key: c.value, type: 'button', role: 'menuitem', tabIndex: tabIdx(flat, x => x.value === p.value)(i), disabled: c.disabled, 'aria-disabled': c.disabled ? 'true' : undefined, 'aria-current': c.value === p.value ? 'page' : undefined, class: 'zg-menu__i', onClick: e => { toTrigger(e); close(); p.onChange && p.onChange(c.value); } },
163
+ c.icon ? icon(h, c.icon, 16) : null, h('span', null, c.label), c.count ? h('span', { class: 'zg-navitem__count' }, c.count) : null))) : null);
164
+ };
165
+ const measure = el => tnMeasure(el, { items, more, set, state, collapse });
166
+ return h('header', { class: 'zg-topnav' }, h('div', { class: 'zg-topnav__brand' }, p.brand),
167
+ h('nav', { class: 'zg-topnav__items', 'data-overflow': collapse ? 'menu' : 'none', 'aria-label': t('TopNav', 'label'), ref: measure }, ...shown.map(entry), more ? entry(more) : null),
168
+ h('div', { class: 'zg-topnav__actions' }, slots.actions || p.actions || null)); } },
120
169
  SideNav: { render: ({ h, props: p, slots }) => h('aside', { class: cx('zg-sidenav', p.collapsed && 'zg-sidenav--collapsed') }, h('div', { class: 'zg-sidenav__brand' }, p.brand), ...(p.items || []).map(it => it.group ? h('div', { key: it.group, class: 'zg-sidenav__group', style: p.collapsed ? { display: 'none' } : undefined }, it.group) : nav(h, it, p.value, p.onChange, p.collapsed)), h('div', { class: 'zg-sidenav__sp' }), slots.footer || p.footer || null,
121
170
  (p.onToggle || p.collapsible) ? h('button', { type: 'button', class: 'zg-navitem', onClick: p.onToggle, 'aria-label': t('SideNav', 'toggle') }, icon(h, p.collapsed ? 'panel-left-open' : 'panel-left-close', 18), h('span', null, p.collapseLabel || t('SideNav', 'collapse'))) : null) },
122
171
  AppShell: { state: { collapsed: false }, render: ({ C, h, props: p, children, slots, state, set }) => {
@@ -1,5 +1,5 @@
1
1
  export default {
2
- en: { label: 'Main' },
3
- ar: { label: 'الرئيسية' },
4
- ru: { label: 'Основная' },
2
+ en: { label: 'Main', more: 'More' },
3
+ ar: { label: 'الرئيسية', more: 'المزيد' },
4
+ ru: { label: 'Основная', more: 'Ещё' },
5
5
  };
@@ -27,7 +27,10 @@ export default {
27
27
  "component": "<svg class=\"lucide lucide-component\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M15.536 11.293a1 1 0 0 0 0 1.414l2.376 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0z\" /><path d=\"M2.297 11.293a1 1 0 0 0 0 1.414l2.377 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414L6.088 8.916a1 1 0 0 0-1.414 0z\" /><path d=\"M8.916 17.912a1 1 0 0 0 0 1.415l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.415l-2.377-2.376a1 1 0 0 0-1.414 0z\" /><path d=\"M8.916 4.674a1 1 0 0 0 0 1.414l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0z\" /></svg>",
28
28
  "contact": "<svg class=\"lucide lucide-contact\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M16 2v2\" /><path d=\"M7 22v-2a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v2\" /><path d=\"M8 2v2\" /><circle cx=\"12\" cy=\"11\" r=\"3\" /><rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" /></svg>",
29
29
  "copy": "<svg class=\"lucide lucide-copy\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" /><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" /></svg>",
30
+ "currency": "<svg class=\"lucide lucide-currency\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><circle cx=\"12\" cy=\"12\" r=\"8\" /><line x1=\"3\" x2=\"6\" y1=\"3\" y2=\"6\" /><line x1=\"21\" x2=\"18\" y1=\"3\" y2=\"6\" /><line x1=\"3\" x2=\"6\" y1=\"21\" y2=\"18\" /><line x1=\"21\" x2=\"18\" y1=\"21\" y2=\"18\" /></svg>",
31
+ "donut": "<svg class=\"lucide lucide-donut\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M20.5 10a2.5 2.5 0 0 1-2.4-3H18a2.95 2.95 0 0 1-2.6-4.4 10 10 0 1 0 6.3 7.1c-.3.2-.8.3-1.2.3\" /><circle cx=\"12\" cy=\"12\" r=\"3\" /></svg>",
30
32
  "dot": "<svg class=\"lucide lucide-dot\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><circle cx=\"12.1\" cy=\"12.1\" r=\"1\" /></svg>",
33
+ "ellipsis": "<svg class=\"lucide lucide-ellipsis\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><circle cx=\"12\" cy=\"12\" r=\"1\" /><circle cx=\"19\" cy=\"12\" r=\"1\" /><circle cx=\"5\" cy=\"12\" r=\"1\" /></svg>",
31
34
  "ellipsis-vertical": "<svg class=\"lucide lucide-ellipsis-vertical\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><circle cx=\"12\" cy=\"12\" r=\"1\" /><circle cx=\"12\" cy=\"5\" r=\"1\" /><circle cx=\"12\" cy=\"19\" r=\"1\" /></svg>",
32
35
  "expand": "<svg class=\"lucide lucide-expand\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"m21 21-6-6m6 6v-4.8m0 4.8h-4.8\" /><path d=\"M3 16.2V21m0 0h4.8M3 21l6-6\" /><path d=\"M21 7.8V3m0 0h-4.8M21 3l-6 6\" /><path d=\"M3 7.8V3m0 0h4.8M3 3l6 6\" /></svg>",
33
36
  "external-link": "<svg class=\"lucide lucide-external-link\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M15 3h6v6\" /><path d=\"M10 14 21 3\" /><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" /></svg>",
@@ -37,6 +40,7 @@ export default {
37
40
  "file-check-2": "<svg class=\"lucide lucide-file-check-2\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\" /><path d=\"M14 2v4a2 2 0 0 0 2 2h4\" /><path d=\"m3 15 2 2 4-4\" /></svg>",
38
41
  "file-code-2": "<svg class=\"lucide lucide-file-code-2\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\" /><path d=\"M14 2v4a2 2 0 0 0 2 2h4\" /><path d=\"m5 12-3 3 3 3\" /><path d=\"m9 18 3-3-3-3\" /></svg>",
39
42
  "filter": "<svg class=\"lucide lucide-filter\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\" /></svg>",
43
+ "focus": "<svg class=\"lucide lucide-focus\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><circle cx=\"12\" cy=\"12\" r=\"3\" /><path d=\"M3 7V5a2 2 0 0 1 2-2h2\" /><path d=\"M17 3h2a2 2 0 0 1 2 2v2\" /><path d=\"M21 17v2a2 2 0 0 1-2 2h-2\" /><path d=\"M7 21H5a2 2 0 0 1-2-2v-2\" /></svg>",
40
44
  "frame": "<svg class=\"lucide lucide-frame\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><line x1=\"22\" x2=\"2\" y1=\"6\" y2=\"6\" /><line x1=\"22\" x2=\"2\" y1=\"18\" y2=\"18\" /><line x1=\"6\" x2=\"6\" y1=\"2\" y2=\"22\" /><line x1=\"18\" x2=\"18\" y1=\"2\" y2=\"22\" /></svg>",
41
45
  "ghost": "<svg class=\"lucide lucide-ghost\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M9 10h.01\" /><path d=\"M15 10h.01\" /><path d=\"M12 2a8 8 0 0 0-8 8v12l3-3 2.5 2.5L12 19l2.5 2.5L17 19l3 3V10a8 8 0 0 0-8-8z\" /></svg>",
42
46
  "grip-vertical": "<svg class=\"lucide lucide-grip-vertical\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><circle cx=\"9\" cy=\"12\" r=\"1\" /><circle cx=\"9\" cy=\"5\" r=\"1\" /><circle cx=\"9\" cy=\"19\" r=\"1\" /><circle cx=\"15\" cy=\"12\" r=\"1\" /><circle cx=\"15\" cy=\"5\" r=\"1\" /><circle cx=\"15\" cy=\"19\" r=\"1\" /></svg>",
@@ -62,12 +66,14 @@ export default {
62
66
  "panel-left-close": "<svg class=\"lucide lucide-panel-left-close\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" /><path d=\"M9 3v18\" /><path d=\"m16 15-3-3 3-3\" /></svg>",
63
67
  "panel-left-open": "<svg class=\"lucide lucide-panel-left-open\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" /><path d=\"M9 3v18\" /><path d=\"m14 9 3 3-3 3\" /></svg>",
64
68
  "pencil": "<svg class=\"lucide lucide-pencil\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\" /><path d=\"m15 5 4 4\" /></svg>",
69
+ "percent": "<svg class=\"lucide lucide-percent\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><line x1=\"19\" x2=\"5\" y1=\"5\" y2=\"19\" /><circle cx=\"6.5\" cy=\"6.5\" r=\"2.5\" /><circle cx=\"17.5\" cy=\"17.5\" r=\"2.5\" /></svg>",
65
70
  "phone": "<svg class=\"lucide lucide-phone\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\" /></svg>",
66
71
  "phone-off": "<svg class=\"lucide lucide-phone-off\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-3.33-2.67m-2.67-3.34a19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91\" /><line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\" /></svg>",
67
72
  "pill": "<svg class=\"lucide lucide-pill\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"m10.5 20.5 10-10a4.95 4.95 0 1 0-7-7l-10 10a4.95 4.95 0 1 0 7 7Z\" /><path d=\"m8.5 8.5 7 7\" /></svg>",
68
73
  "play": "<svg class=\"lucide lucide-play\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><polygon points=\"6 3 20 12 6 21 6 3\" /></svg>",
69
74
  "plus": "<svg class=\"lucide lucide-plus\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M5 12h14\" /><path d=\"M12 5v14\" /></svg>",
70
75
  "pointer": "<svg class=\"lucide lucide-pointer\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M22 14a8 8 0 0 1-8 8\" /><path d=\"M18 11v-1a2 2 0 0 0-2-2a2 2 0 0 0-2 2\" /><path d=\"M14 10V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1\" /><path d=\"M10 9.5V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v10\" /><path d=\"M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15\" /></svg>",
76
+ "presentation": "<svg class=\"lucide lucide-presentation\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M2 3h20\" /><path d=\"M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3\" /><path d=\"m7 21 5-5 5 5\" /></svg>",
71
77
  "quote": "<svg class=\"lucide lucide-quote\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z\" /><path d=\"M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z\" /></svg>",
72
78
  "radio": "<svg class=\"lucide lucide-radio\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M4.9 19.1C1 15.2 1 8.8 4.9 4.9\" /><path d=\"M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5\" /><circle cx=\"12\" cy=\"12\" r=\"2\" /><path d=\"M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5\" /><path d=\"M19.1 4.9C23 8.8 23 15.1 19.1 19\" /></svg>",
73
79
  "refresh-cw": "<svg class=\"lucide lucide-refresh-cw\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ><path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" /><path d=\"M21 3v5h-5\" /><path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" /><path d=\"M8 16H3v5\" /></svg>",
package/src/meta/data.js CHANGED
@@ -6,6 +6,33 @@ const TONE = "'brand' | 'sky' | 'success' | 'warning' | 'danger' | 'neutral'";
6
6
  const size = (d, extra) => ({ type: SIZE, values: SZ, default: d || 'md', description: extra || 'Component scale.' });
7
7
  const delta = (d) => ({ type: 'string', description: d || 'Change indicator such as "+12%" or "-3.4%". A leading "-" renders as a decrease, anything else as an increase.' });
8
8
 
9
+ const SERIES = '{ key: string; label: string; values: { x: string | number | Date; y: number | null }[]; tone?: \'brand\' | \'success\' | \'warning\' | \'danger\' | \'info\' | \'neutral\'; dashed?: boolean }[]';
10
+ const CHART_TONE = "'brand' | 'success' | 'warning' | 'danger' | 'info' | 'neutral'";
11
+ const chartCommon = (kind) => ({
12
+ unit: { type: "'count' | 'currency' | 'percent'", values: ['count', 'currency', 'percent'], default: 'count', description: 'Number format of the value axis, tooltip and ' + (kind === 'donut' ? 'centre total' : 'legend') + '. count: grouped digits, compact on the axis (1.2k, 1.2M, localized by Intl); currency: needs currency; percent: values are percent points (12 means 12%).' },
13
+ currency: { type: 'string', description: 'ISO 4217 code for unit="currency", for example UZS, SAR or USD. The axis stays compact without the symbol; tooltip and legend show the full amount.' },
14
+ yFormat: { type: '(n: number) => string', description: 'Custom formatter for every number (axis, tooltip' + (kind === 'donut' ? ', total' : '') + '). Overrides unit and currency.' },
15
+ locale: { type: 'string', description: 'BCP 47 tag for numbers and dates, for example "ar" or "uz-UZ". Defaults to the kit locale (en ru uz ar tr and the rest). An RTL language also mirrors the chart.' },
16
+ dir: { type: "'ltr' | 'rtl'", values: ['ltr', 'rtl'], description: 'Text direction. Defaults to the direction of the page (RTL mirrors the axis, the plot and the tooltip).' },
17
+ label: { type: 'string', description: 'Accessible name, for example "Revenue". The generated summary (series, period, minimum, maximum, last value) follows it in the aria-label.' },
18
+ height: { type: 'number', description: kind === 'donut' ? 'Diameter in px. Overrides size.' : 'Chart height in px (without the legend). Overrides size. Horizontal bars grow with the number of categories when it is not set.' },
19
+ size: { type: SIZE, values: SZ, default: 'md', description: kind === 'donut' ? 'Preset diameter: xs 120, sm 160, md 200, lg 240, xl 300 px.' : 'Preset height: xs 140, sm 180, md 240, lg 320, xl 400 px.' },
20
+ loading: { type: 'boolean', default: false, description: 'Shows a skeleton in the shape of the chart.' },
21
+ emptyText: { type: 'string', description: 'Text of the empty state. Default is the translated "No data for this period".' },
22
+ emptyIcon: { type: 'string', description: 'Icon name shown above the empty text. None by default.' },
23
+ legend: { type: 'boolean', default: kind === 'donut' ? true : 'true when there is more than one series', description: 'Text legend of toggle buttons (aria-pressed). A button hides and shows its ' + (kind === 'donut' ? 'slice' : 'series') + '; the scale is recomputed.' },
24
+ });
25
+ const chartEvents = (kind) => ({
26
+ pointClick: { line: 'Click or Enter on a point. Called with { seriesKey, series, index, x, label, y }: the series under the pointer (or the keyboard series), the index in the shared x list, the original x value, its formatted label and y.', donut: 'Click or Enter on a slice. Called with { key, label, value, share, index }; share is 0 to 1.' }[kind === 'donut' ? 'donut' : 'line'],
27
+ legendToggle: 'A legend button was pressed. Called with { key, label, hidden } where hidden is the new state.',
28
+ });
29
+ const chartNotes = [
30
+ 'Pure inline SVG, no dependencies. Colors come from the --zg-chart-* tokens (light, dark, gold and tamam), so there is nothing to configure per theme.',
31
+ 'Empty data (no points, only null values, or only zeros for bars and donut) shows the empty state instead of an axis of zeros. A null y is a gap in the line, never a zero. All values equal: the axis starts at 0 and leaves headroom.',
32
+ 'Accessibility: the plot is one focusable role="img" with a generated aria-label, a visually hidden data table, and a live region that reads the point while you move. Keys: Left and Right (mirrored in RTL) move between points, Up and Down between series, Home and End jump, Escape hides the tooltip, Enter fires pointClick.',
33
+ 'The chart fills the width of its container (ResizeObserver, client only) and renders at 640 px on the server.',
34
+ ];
35
+
9
36
  export default {
10
37
  Card: {
11
38
  description: 'Glass surface container with an optional title header, used to group related content.',
@@ -212,6 +239,56 @@ export default {
212
239
  events: { menu: 'Called with the clicked menu item label (the string from menu); the menu closes afterwards.' },
213
240
  },
214
241
 
242
+ LineChart: {
243
+ description: 'Line chart for time series with several series, a localized time axis with automatic label step, "nice" value ticks, gaps for null values, tooltip and keyboard navigation.',
244
+ props: {
245
+ series: { type: SERIES, required: true, description: 'Series to draw. x is a Date, an ISO date string (day, week, month are detected from the spacing), a number or a category string; the series share one x list, a missing x or a null y is a gap. tone is brand, success, warning, danger, info or neutral; without it the series palette (--zg-chart-1 to 6) is used. dashed draws the line dashed, for example the previous week.' },
246
+ ...chartCommon('line'),
247
+ yMin: { type: 'number', description: 'Fixed lower end of the value axis (otherwise nice ticks from the data, from 0 unless the data stays far above it).' },
248
+ yMax: { type: 'number', description: 'Fixed upper end of the value axis.' },
249
+ showPoints: { type: 'boolean', description: 'Dots on every point. Default: on up to 40 points; isolated points (between gaps) always get a dot.' },
250
+ },
251
+ events: chartEvents('line'),
252
+ notes: chartNotes,
253
+ },
254
+
255
+ AreaChart: {
256
+ description: 'Line chart with a gradient fill under each line, made from the series color; same data, axes, tooltip and keyboard behavior as LineChart.',
257
+ props: {
258
+ series: { type: SERIES, required: true, description: 'Series to draw, see LineChart. The fill goes down to the zero line (or the bottom of the plot when the axis does not include 0); each run between gaps is filled separately.' },
259
+ ...chartCommon('area'),
260
+ yMin: { type: 'number', description: 'Fixed lower end of the value axis.' },
261
+ yMax: { type: 'number', description: 'Fixed upper end of the value axis.' },
262
+ showPoints: { type: 'boolean', description: 'Dots on every point. Default: on up to 40 points; isolated points always get a dot.' },
263
+ },
264
+ events: chartEvents('line'),
265
+ notes: chartNotes,
266
+ },
267
+
268
+ BarChart: {
269
+ description: 'Vertical or horizontal bar chart, grouped or stacked, for categories such as days, 24 hours or revenue per branch; same axes, formats and accessibility as LineChart.',
270
+ props: {
271
+ series: { type: SERIES, required: true, description: 'One entry per bar series. x is the category (string, number or Date); categories keep their order of first appearance, dates are sorted. Negative values draw below the zero line.' },
272
+ orientation: { type: "'vertical' | 'horizontal'", values: ['vertical', 'horizontal'], default: 'vertical', description: 'Horizontal puts the categories on the left (right in RTL) and grows with the number of categories.' },
273
+ stacked: { type: 'boolean', default: false, description: 'Stacks the series of a category (positives and negatives stack apart) instead of placing them side by side; the tooltip adds a total.' },
274
+ ...chartCommon('bar'),
275
+ yMin: { type: 'number', description: 'Fixed lower end of the value axis.' },
276
+ yMax: { type: 'number', description: 'Fixed upper end of the value axis.' },
277
+ },
278
+ events: chartEvents('line'),
279
+ notes: chartNotes,
280
+ },
281
+
282
+ DonutChart: {
283
+ description: 'Donut chart of shares (sales channels, payment methods, plans) with a text legend, the total in the centre and a legend toggle.',
284
+ props: {
285
+ data: { type: "{ key: string; label: string; value: number; tone?: " + CHART_TONE + " }[]", required: true, description: 'Slices. Only positive values draw; others keep their legend row at 0. All zero or empty shows the empty state.' },
286
+ ...chartCommon('donut'),
287
+ },
288
+ events: chartEvents('donut'),
289
+ notes: chartNotes,
290
+ },
291
+
215
292
  Ring: {
216
293
  description: 'Circular progress gauge with a percentage in the center and a tick scale around it.',
217
294
  props: {
@@ -4,7 +4,7 @@ export const GROUPS = [
4
4
  { id: 'layout', title: 'Layout', blurb: 'Stacks, grids and page structure.', names: ['Box', 'Center', 'HStack', 'VStack', 'Grid', 'Divider', 'ScrollArea', 'SplitLayout', 'AppShell', 'PageHeader', 'AuthLayout', 'SettingsSection', 'LiquidGlass', 'Footer', 'Sortable', 'Portal'] },
5
5
  { id: 'actions', title: 'Actions', blurb: 'Buttons and things you press.', names: ['Button', 'IconButton', 'Pressable', 'Fab', 'CopyButton', 'ToggleGroup', 'SegmentedControl', 'FilterChips', 'Chip', 'Tag', 'Badge', 'ActionRail', 'ActionCard', 'NotificationBell'] },
6
6
  { id: 'forms', title: 'Forms', blurb: 'Inputs, pickers and form building blocks.', names: ['TextField', 'Textarea', 'NumberInput', 'PasswordInput', 'SearchInput', 'PhoneInput', 'CodeInput', 'ChipInput', 'InlineEdit', 'RichEditor', 'Select', 'MultiSelect', 'GroupedSelect', 'Combobox', 'TreeSelect', 'PillSelect', 'Checkbox', 'Switch', 'RadioGroup', 'OptionCard', 'OptionCards', 'Slider', 'RangeSlider', 'Rating', 'ColorPicker', 'Calendar', 'RangeCalendar', 'DatePicker', 'TimePicker', 'FileUpload', 'ImageUpload', 'FormField', 'ValidatedField', 'LoginForm', 'StepForm', 'Stepper'] },
7
- { id: 'data', title: 'Data display', blurb: 'Tables, stats, charts and lists.', names: ['Card', 'DataTable', 'DataGroup', 'Descriptions', 'Timeline', 'TreeView', 'CalendarView', 'KpiTile', 'KpiInline', 'StatsCard', 'StatRow', 'StatLine', 'StatStrip', 'CountryRow', 'VersusBar', 'SegBar', 'MiniChart', 'ChartCard', 'Ring', 'OccupancyChart', 'SalesFunnel', 'SystemStatus', 'UserCell', 'IconTile', 'StatusIcon', 'HeroBanner', 'TabTiles', 'PricingCard', 'IntegrationCard'] },
7
+ { id: 'data', title: 'Data display', blurb: 'Tables, stats, charts and lists.', names: ['Card', 'DataTable', 'DataGroup', 'Descriptions', 'Timeline', 'TreeView', 'CalendarView', 'KpiTile', 'KpiInline', 'StatsCard', 'StatRow', 'StatLine', 'StatStrip', 'CountryRow', 'VersusBar', 'SegBar', 'MiniChart', 'ChartCard', 'LineChart', 'AreaChart', 'BarChart', 'DonutChart', 'Ring', 'OccupancyChart', 'SalesFunnel', 'SystemStatus', 'UserCell', 'IconTile', 'StatusIcon', 'HeroBanner', 'TabTiles', 'PricingCard', 'IntegrationCard'] },
8
8
  { id: 'feedback', title: 'Feedback', blurb: 'Alerts, progress and states.', names: ['Alert', 'Banner', 'Toast', 'Snackbar', 'Progress', 'Spinner', 'Skeleton', 'EmptyState', 'ErrorPage', 'StatusPage', 'DataState', 'LoadingOverlay'] },
9
9
  { id: 'overlay', title: 'Overlay', blurb: 'Dialogs, sheets and popovers.', names: ['Modal', 'Drawer', 'ConfirmDialog', 'Popconfirm', 'StepDialog', 'FullscreenDialog', 'BottomSheet', 'Actionsheet', 'Popover', 'HoverCard', 'Tooltip', 'Dropdown', 'Menu', 'ContextMenu', 'CommandBar', 'CommandPalette', 'ImageViewer', 'Tour', 'TourPopover', 'Screensaver'] },
10
10
  { id: 'navigation', title: 'Navigation', blurb: 'Menus, tabs and paging.', names: ['TopNav', 'SideNav', 'SubNav', 'Tabs', 'PillTabs', 'Breadcrumbs', 'Pagination', 'Accordion'] },
@@ -9,13 +9,15 @@ export default {
9
9
  description: 'Horizontal app header with a brand area, navigation items and an actions area.',
10
10
  props: {
11
11
  brand: { type: 'string | VNode', description: 'Logo or product name on the left.' },
12
- items: { type: '{ value: string; label: string; icon?: string; count?: string | number }[]', default: '[]', description: 'Navigation entries. count renders a small counter after the label.' },
12
+ items: { type: '{ value: string; label: string; icon?: string; count?: string | number; children?: ({ value: string; label: string; icon?: string; count?: string | number; disabled?: boolean } | { heading: string } | { separator: true })[] }[]', default: '[]', description: 'Navigation entries. count renders a small counter after the label. An entry with children becomes a drop-down: click or ArrowDown opens a menu of its children (each with an optional icon and count, headings and separators allowed); selecting one calls change with the child value. The entry is marked current when one of its children is.' },
13
13
  value: current,
14
+ overflow: { type: "'menu' | 'none'", values: ['menu', 'none'], default: 'menu', description: "'menu': entries that do not fit move into a trailing \"More\" drop-down (measured after render and on resize; server rendering shows all entries). 'none': no collapsing, the row scrolls sideways and sub-menus are clipped, so avoid children with it." },
15
+ moreLabel: { type: 'string', default: 'More', description: 'Accessible name of the overflow drop-down button.' },
14
16
  size: size('header padding, item height and text'),
15
17
  },
16
18
  slots: { actions: 'Right side of the header (buttons, avatar). React also accepts an actions prop.' },
17
- events: { change: 'Called with the value of the clicked item.' },
18
- notes: ['Controlled: update value yourself in the change handler.', 'Items use count; SubNav uses badge for the same purpose.'],
19
+ events: { change: 'Called with the value of the clicked item or sub-menu item.' },
20
+ notes: ['Controlled: update value yourself in the change handler.', 'Items use count; SubNav uses badge for the same purpose.', 'Entries with children and the More menu share the Dropdown keyboard behaviour: ArrowDown opens, arrows and Home/End move, Escape closes and returns focus to the button.'],
19
21
  },
20
22
 
21
23
  SideNav: {
package/src/meta/tiers.js CHANGED
@@ -4,7 +4,7 @@ export const PRO = {
4
4
  layout: ['LiquidGlass', 'Sortable'],
5
5
  actions: ['Fab', 'ActionRail', 'ActionCard', 'NotificationBell'],
6
6
  forms: ['ChipInput', 'InlineEdit', 'RichEditor', 'GroupedSelect', 'TreeSelect', 'PillSelect', 'OptionCard', 'OptionCards', 'RangeSlider', 'ColorPicker', 'RangeCalendar', 'ImageUpload', 'ValidatedField', 'StepForm', 'Stepper'],
7
- data: ['DataGroup', 'TreeView', 'CalendarView', 'KpiInline', 'StatRow', 'StatLine', 'StatStrip', 'CountryRow', 'VersusBar', 'SegBar', 'ChartCard', 'OccupancyChart', 'SalesFunnel', 'SystemStatus', 'UserCell', 'HeroBanner', 'TabTiles', 'PricingCard', 'IntegrationCard'],
7
+ data: ['DataGroup', 'TreeView', 'CalendarView', 'KpiInline', 'StatRow', 'StatLine', 'StatStrip', 'CountryRow', 'VersusBar', 'SegBar', 'ChartCard', 'LineChart', 'AreaChart', 'BarChart', 'DonutChart', 'OccupancyChart', 'SalesFunnel', 'SystemStatus', 'UserCell', 'HeroBanner', 'TabTiles', 'PricingCard', 'IntegrationCard'],
8
8
  overlay: ['StepDialog', 'FullscreenDialog', 'Actionsheet', 'HoverCard', 'CommandBar', 'ImageViewer', 'Tour', 'TourPopover', 'Screensaver'],
9
9
  media: ['Gallery', 'Map', 'ProfileHeader', 'ChatAi', 'Carousel'],
10
10
  domain: ['HotelCard', 'TourCard', 'PrayerTimes', 'RamadanTracker', 'QiblaCompass', 'BookingCard', 'GuestCard', 'RoomGrid', 'RoomCalendar', 'LeadCard', 'DealCard', 'PipelineBoard', 'ContactPanel', 'KanbanColumn', 'KanbanCard', 'CallRow', 'CallCard', 'IncomingCall', 'AuthShowcase'],
@@ -0,0 +1,68 @@
1
+ /* LineChart, AreaChart, BarChart, DonutChart (@ziyarago/ui-pro). Colors only from tokens: the series palette (--zg-chart-1..6), the tones and the grid / axis / label colors are defined here
2
+ per theme and palette (gold, tamam). Tokens are declared on .zg-chart so palette references resolve where the chart is. */
3
+ .zg-chart{
4
+ --zg-chart-brand:var(--zg-brand);--zg-chart-info:var(--zg-sky);--zg-chart-success:oklch(0.76 0.15 150);--zg-chart-warning:oklch(0.8 0.14 80);--zg-chart-danger:oklch(0.7 0.18 25);--zg-chart-neutral:oklch(0.72 0.012 70);
5
+ --zg-chart-1:var(--zg-chart-brand);--zg-chart-2:var(--zg-chart-info);--zg-chart-3:var(--zg-chart-success);--zg-chart-4:oklch(0.72 0.13 300);--zg-chart-5:oklch(0.76 0.14 45);--zg-chart-6:oklch(0.72 0.15 350);
6
+ --zg-chart-grid:oklch(1 0 0 / .07);--zg-chart-axis:oklch(1 0 0 / .24);--zg-chart-label:var(--zg-text-3);--zg-chart-surface:var(--zg-pop);--zg-chart-band:oklch(1 0 0 / .06);
7
+ }
8
+ [data-palette="tamam"] .zg-chart{--zg-chart-success:oklch(0.76 0.13 175);--zg-chart-1:var(--zg-chart-brand);--zg-chart-2:var(--zg-chart-info);--zg-chart-3:oklch(0.72 0.13 300);--zg-chart-4:oklch(0.78 0.14 75);--zg-chart-5:oklch(0.72 0.15 350);--zg-chart-6:oklch(0.7 0.14 255)}
9
+ [data-theme="light"] .zg-chart{
10
+ --zg-chart-brand:oklch(0.55 0.09 66);--zg-chart-info:oklch(0.5 0.11 230);--zg-chart-success:oklch(0.48 0.13 150);--zg-chart-warning:oklch(0.52 0.12 70);--zg-chart-danger:oklch(0.5 0.18 25);--zg-chart-neutral:oklch(0.5 0.012 70);
11
+ --zg-chart-4:oklch(0.5 0.15 300);--zg-chart-5:oklch(0.55 0.14 45);--zg-chart-6:oklch(0.5 0.17 350);
12
+ --zg-chart-grid:oklch(0.3 0.02 60 / .26);--zg-chart-axis:oklch(0.3 0.02 60 / .75);--zg-chart-band:oklch(0.3 0.02 60 / .06);
13
+ }
14
+ [data-theme="light"][data-palette="tamam"] .zg-chart,[data-theme="light"] [data-palette="tamam"] .zg-chart,[data-palette="tamam"] [data-theme="light"] .zg-chart{--zg-chart-brand:oklch(0.5 0.17 142);--zg-chart-info:oklch(0.5 0.1 200);--zg-chart-success:oklch(0.48 0.12 175);--zg-chart-3:oklch(0.5 0.15 300);--zg-chart-4:oklch(0.55 0.13 70);--zg-chart-5:oklch(0.5 0.17 350);--zg-chart-6:oklch(0.5 0.15 255)}
15
+
16
+ .zg-chart{position:relative;display:block;width:100%;min-width:0;color:var(--zg-text-2);font:12px/1.3 var(--zg-font)}
17
+ .zg-chart__box{position:relative;max-width:100%}
18
+ .zg-chart__plot{position:relative;display:block;max-width:100%;touch-action:pan-y;outline:0;border-radius:10px;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;cursor:crosshair}
19
+ .zg-chart__plot:focus-visible{box-shadow:0 0 0 3px var(--zg-brand-soft),0 0 0 1px var(--zg-brand)}
20
+ .zg-chart__svg{display:block;width:100%;height:auto;max-width:100%;overflow:visible}
21
+ .zg-chart__grid{stroke:var(--zg-chart-grid);stroke-width:1;shape-rendering:crispEdges}
22
+ .zg-chart__zero{stroke:var(--zg-chart-axis)}
23
+ .zg-chart__tick{fill:var(--zg-chart-label);font-size:11px;font-family:inherit;font-variant-numeric:tabular-nums}
24
+ .zg-chart__line{stroke:var(--c);stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round}
25
+ .zg-chart__gs{stop-color:var(--c)}
26
+ .zg-chart__pt{fill:var(--c);stroke:var(--zg-chart-surface);stroke-width:1.5}
27
+ .zg-chart__pt--on{stroke-width:2}
28
+ .zg-chart__guide{stroke:var(--zg-chart-axis);stroke-width:1;stroke-dasharray:3 3;shape-rendering:crispEdges}
29
+ .zg-chart__band{fill:var(--zg-chart-band)}
30
+ .zg-chart__bar{fill:var(--c)}
31
+ .zg-chart__bar--on{fill:color-mix(in oklch,var(--c) 82%,var(--zg-text-1))}
32
+ .zg-chart__slice{fill:var(--c);stroke:none}
33
+ .zg-chart__slice--on{fill:color-mix(in oklch,var(--c) 82%,var(--zg-text-1))}
34
+ .zg-chart__track{stroke:var(--zg-s2)}
35
+ .zg-chart__total{fill:var(--zg-text-1);font-weight:600;font-family:inherit;font-variant-numeric:tabular-nums}
36
+ .zg-chart__totall{fill:var(--zg-chart-label);font-size:11px;font-family:inherit}
37
+ .zg-chart__tip{position:absolute;z-index:3;box-sizing:border-box;pointer-events:none;background:var(--zg-pop);color:var(--zg-text-1);border-radius:12px;box-shadow:var(--zg-shadow-1),inset 0 0 0 1px var(--zg-stroke);padding:8px 12px;font-size:12px}
38
+ .zg-chart__tipt{color:var(--zg-text-2);margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
39
+ .zg-chart__tipr{display:flex;align-items:center;gap:8px;line-height:20px}
40
+ .zg-chart__tipr--on .zg-chart__tipl{color:var(--zg-text-1)}
41
+ .zg-chart__tipl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--zg-text-2)}
42
+ .zg-chart__tipv{font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
43
+ .zg-chart__sw{flex:none;width:10px;height:10px;border-radius:3px;background:var(--c)}
44
+ .zg-chart__sw--dash{width:16px;height:0;border-radius:0;background:none;border-top:2.5px dashed var(--c)}
45
+ .zg-chart__legend{display:flex;flex-wrap:wrap;gap:4px 8px;margin:10px 0 0;padding:0;list-style:none}
46
+ .zg-chart__lg{display:inline-flex;align-items:center;gap:8px;min-height:28px;padding:2px 8px;border:0;border-radius:8px;background:none;color:var(--zg-text-2);font:inherit;font-size:13px;cursor:pointer;text-align:start}
47
+ .zg-chart__lg:hover{background:var(--zg-s2)}
48
+ .zg-chart__lg:focus-visible{outline:0;box-shadow:0 0 0 3px var(--zg-brand-soft),0 0 0 1px var(--zg-brand)}
49
+ .zg-chart__lg[aria-pressed="false"]{color:var(--zg-text-3)}
50
+ .zg-chart__lg[aria-pressed="false"] .zg-chart__lgt{text-decoration:line-through}
51
+ .zg-chart__lg[aria-pressed="false"] .zg-chart__sw{background:none;box-shadow:inset 0 0 0 2px var(--c)}
52
+ .zg-chart__lg[aria-pressed="false"] .zg-chart__sw--dash{box-shadow:none}
53
+ .zg-chart__lgv{margin-inline-start:auto;padding-inline-start:12px;font-variant-numeric:tabular-nums;color:var(--zg-text-2);white-space:nowrap}
54
+ .zg-chart--donut{display:flex;flex-wrap:wrap;align-items:center;gap:12px 28px}
55
+ .zg-chart--donut .zg-chart__box{flex:none;max-width:100%}
56
+ .zg-chart--donut .zg-chart__legend{flex:1 1 200px;min-width:0;flex-direction:column;flex-wrap:nowrap;gap:2px;margin:0}
57
+ .zg-chart--donut .zg-chart__lg{width:100%}
58
+ .zg-chart__empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;box-sizing:border-box;padding:16px;text-align:center;color:var(--zg-text-3);border-radius:16px;box-shadow:inset 0 0 0 1px var(--zg-stroke)}
59
+ .zg-chart__emptyt{margin:0;font-size:13px}
60
+ .zg-chart--donut.zg-chart--empty,.zg-chart--donut.zg-chart--loading{display:block}
61
+ .zg-chart__sk{display:block;max-width:100%}
62
+ .zg-chart--donut .zg-chart__sk{margin-inline:auto}
63
+ .zg-chart__skg{stroke:var(--zg-s2);stroke-width:1;vector-effect:non-scaling-stroke}
64
+ .zg-chart__skp{stroke:var(--zg-s3);stroke-linecap:round;animation:zg-chart-pulse 1.4s ease-in-out infinite}
65
+ .zg-chart__skbars{display:flex;align-items:flex-end;gap:8px}
66
+ .zg-chart__skbars .zg-skel{flex:1;border-radius:6px 6px 2px 2px}
67
+ @keyframes zg-chart-pulse{50%{opacity:.45}}
68
+ @media (prefers-reduced-motion:reduce){.zg-chart__skp{animation:none}}
@@ -166,4 +166,4 @@
166
166
  .zg-call{display:grid;grid-template-columns:36px minmax(0,1fr) auto;gap:12px;align-items:center;padding:10px 12px;border-radius:18px;background:var(--zg-s2)}
167
167
  @media(prefers-reduced-motion:reduce){.zg-root *{animation-duration:.01ms!important;transition-duration:.01ms!important}}
168
168
 
169
- .zg-topnav__items{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 24px),transparent);mask-image:linear-gradient(90deg,#000 calc(100% - 24px),transparent)}.zg-topnav__items:not(:hover){}.zg-topnav__items .zg-navitem{padding-inline:12px}
169
+ .zg-topnav__items .zg-navitem{padding-inline:12px}
@@ -120,3 +120,9 @@
120
120
  .zg-tip--left:hover::before,.zg-tip--right:hover::before{top:0;bottom:0;height:auto;width:10px}
121
121
  .zg-tip--left:hover::before{left:auto;right:100%}.zg-tip--right:hover::before{right:auto;left:100%}
122
122
  .zg-tip[data-tip-off]::after{opacity:0!important;pointer-events:none!important}.zg-tip[data-tip-off]::before{display:none}
123
+ /* TopNav: no edge fade; items that do not fit move into a "More" menu, sub-menus open under their item */
124
+ .zg-topnav__items{overflow:visible;flex-wrap:nowrap}.zg-topnav__items[data-overflow="none"]{overflow-x:auto}
125
+ .zg-topnav__dd{display:flex}.zg-topnav__dd>.zg-navitem{height:100%}.zg-topnav__dd .zg-menu{top:calc(100% + 10px);min-width:220px}
126
+ .zg-topnav__dd>.zg-navitem[aria-expanded="true"] .zg-icon:last-child:not(:first-child){transform:rotate(180deg)}
127
+ .zg-menu__i[aria-current="page"]{background:var(--zg-s2);font-weight:700}.zg-menu__i .zg-navitem__count{margin-inline-start:auto}
128
+ .zg-menu__h{padding:8px 12px 4px;font:600 12px var(--zg-font);letter-spacing:.06em;text-transform:uppercase;color:var(--zg-text-3)}
@@ -8,6 +8,7 @@
8
8
  @import './components6.css';
9
9
  @import './components7.css';
10
10
  @import './components8.css';
11
+ @import './charts.css';
11
12
  @import './sizes/typography.css';
12
13
  @import './sizes/layout.css';
13
14
  @import './sizes/actions.css';
package/types/react.d.ts CHANGED
@@ -3695,24 +3695,35 @@ export const UiTooltip: ComponentType<UiTooltipProps>;
3695
3695
  * Horizontal app header with a brand area, navigation items and an actions area.
3696
3696
  * Note: Controlled: update value yourself in the change handler.
3697
3697
  * Note: Items use count; SubNav uses badge for the same purpose.
3698
+ * Note: Entries with children and the More menu share the Dropdown keyboard behaviour: ArrowDown opens, arrows and Home/End move, Escape closes and returns focus to the button.
3698
3699
  */
3699
3700
  export interface UiTopNavProps {
3700
3701
  /** Logo or product name on the left. */
3701
3702
  brand?: string | ReactNode;
3702
3703
  /**
3703
- * Navigation entries. count renders a small counter after the label.
3704
+ * Navigation entries. count renders a small counter after the label. An entry with children becomes a drop-down: click or ArrowDown opens a menu of its children (each with an optional icon and count, headings and separators allowed); selecting one calls change with the child value. The entry is marked current when one of its children is.
3704
3705
  * @default []
3705
3706
  */
3706
- items?: { value: string; label: string; icon?: string; count?: string | number }[];
3707
+ items?: { value: string; label: string; icon?: string; count?: string | number; children?: ({ value: string; label: string; icon?: string; count?: string | number; disabled?: boolean } | { heading: string } | { separator: true })[] }[];
3707
3708
  /** Value of the active item (aria-current="page"). */
3708
3709
  value?: string;
3710
+ /**
3711
+ * 'menu': entries that do not fit move into a trailing "More" drop-down (measured after render and on resize; server rendering shows all entries). 'none': no collapsing, the row scrolls sideways and sub-menus are clipped, so avoid children with it.
3712
+ * @default 'menu'
3713
+ */
3714
+ overflow?: 'menu' | 'none';
3715
+ /**
3716
+ * Accessible name of the overflow drop-down button.
3717
+ * @default 'More'
3718
+ */
3719
+ moreLabel?: string;
3709
3720
  /**
3710
3721
  * Scale of header padding, item height and text.
3711
3722
  * @default 'md'
3712
3723
  */
3713
3724
  size?: Size;
3714
3725
  /**
3715
- * Called with the value of the clicked item.
3726
+ * Called with the value of the clicked item or sub-menu item.
3716
3727
  * @event change
3717
3728
  */
3718
3729
  onChange?: (value: string) => void;
package/types/vue.d.ts CHANGED
@@ -3727,24 +3727,35 @@ export const UiTooltip: UiVueComponent<UiTooltipProps, UiTooltipSlots>;
3727
3727
  * Horizontal app header with a brand area, navigation items and an actions area.
3728
3728
  * Note: Controlled: update value yourself in the change handler.
3729
3729
  * Note: Items use count; SubNav uses badge for the same purpose.
3730
+ * Note: Entries with children and the More menu share the Dropdown keyboard behaviour: ArrowDown opens, arrows and Home/End move, Escape closes and returns focus to the button.
3730
3731
  */
3731
3732
  export interface UiTopNavProps {
3732
3733
  /** Logo or product name on the left. */
3733
3734
  brand?: string | VNode;
3734
3735
  /**
3735
- * Navigation entries. count renders a small counter after the label.
3736
+ * Navigation entries. count renders a small counter after the label. An entry with children becomes a drop-down: click or ArrowDown opens a menu of its children (each with an optional icon and count, headings and separators allowed); selecting one calls change with the child value. The entry is marked current when one of its children is.
3736
3737
  * @default []
3737
3738
  */
3738
- items?: { value: string; label: string; icon?: string; count?: string | number }[];
3739
+ items?: { value: string; label: string; icon?: string; count?: string | number; children?: ({ value: string; label: string; icon?: string; count?: string | number; disabled?: boolean } | { heading: string } | { separator: true })[] }[];
3739
3740
  /** Value of the active item (aria-current="page"). */
3740
3741
  value?: string;
3742
+ /**
3743
+ * 'menu': entries that do not fit move into a trailing "More" drop-down (measured after render and on resize; server rendering shows all entries). 'none': no collapsing, the row scrolls sideways and sub-menus are clipped, so avoid children with it.
3744
+ * @default 'menu'
3745
+ */
3746
+ overflow?: 'menu' | 'none';
3747
+ /**
3748
+ * Accessible name of the overflow drop-down button.
3749
+ * @default 'More'
3750
+ */
3751
+ moreLabel?: string;
3741
3752
  /**
3742
3753
  * Scale of header padding, item height and text.
3743
3754
  * @default 'md'
3744
3755
  */
3745
3756
  size?: Size;
3746
3757
  /**
3747
- * Called with the value of the clicked item.
3758
+ * Called with the value of the clicked item or sub-menu item.
3748
3759
  * @event change
3749
3760
  */
3750
3761
  onChange?: (value: string) => void;