@muten/shadcn 0.2.10 → 0.2.11

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.
package/kit.css CHANGED
@@ -780,7 +780,7 @@ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webki
780
780
  .cx-dt td.cx-dt-act { display: none; }
781
781
  .cx-dt-who small { display: none; }
782
782
  .cx-dt td.cx-dt-svc[data-when]:not([data-when=""])::after { content: " · " attr(data-when); }
783
- .cx-dt td.cx-dt-svc ~ td.cx-dt-svc::after { content: none; }
783
+ .cx-dt td.cx-dt-svc ~ td.cx-dt-svc[data-when]::after { content: none; }
784
784
  }
785
785
 
786
786
  /* ── Board ── */
@@ -5548,3 +5548,15 @@ a.cx-bc-item:hover { color: var(--foreground); }
5548
5548
 
5549
5549
  /* ── Field: a field that must be filled says so at its label ── */
5550
5550
  .cn-label.cx-required::after { content: " *"; color: var(--destructive); }
5551
+
5552
+ /* ── Alert with an action: the action takes its own column (Nova's absolute corner reserves 72 px, and a wider button
5553
+ covered the title); on a phone it goes under the text ── */
5554
+ .style-nova .cn-alert:has(> .cn-alert-action) { padding-right: 10px; grid-template-columns: 1fr auto; }
5555
+ .style-nova .cn-alert:has(> :is(svg, .mu-icon)):has(> .cn-alert-action) { grid-template-columns: auto 1fr auto; }
5556
+ .style-nova .cn-alert > .cn-alert-action { position: static; grid-column: -2; grid-row: 1 / span 9; align-self: center; margin-left: 8px; }
5557
+ @media (max-width: 560px) {
5558
+ .style-nova .cn-alert:has(> .cn-alert-action) { grid-template-columns: 1fr; }
5559
+ .style-nova .cn-alert:has(> :is(svg, .mu-icon)):has(> .cn-alert-action) { grid-template-columns: auto 1fr; }
5560
+ .style-nova .cn-alert > .cn-alert-action { grid-column: 1 / -1; grid-row: auto; justify-self: start; margin: 8px 0 0; }
5561
+ .style-nova .cn-alert:has(> :is(svg, .mu-icon)) > .cn-alert-action { grid-column: 2; }
5562
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muten/shadcn",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "shadcn-style component registry for muten - own-the-source parts you add with `muten add`.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -2,7 +2,7 @@
2
2
  // and bulk actions are data. Everything else is the artifact's: filters from their type (search · facet · date ·
3
3
  // range) with counts, chips that leave with a fade, rows that fade out / slide / enter, the height easing, sort by
4
4
  // header, selection with its own bulk bar, the phone's “search + Filters” sheet, and a narrow box becoming Items.
5
- // columns [{key, label, type: person|text|date|status|money, sub (person: the key under the name), sortable}]
5
+ // columns [{key, label, type: person|text|date|status|money, sub (person: the key under the name), sortable, sort: "desc" (opens newest first)}]
6
6
  // rows [{id, …keys}] (a date is "yyyy-mm-ddTHH:MM"; a row's date may be "+N" days from today too)
7
7
  // filters [{id, type: search|facet|date|range, label, key | keys (search)}]
8
8
  // statuses [{id, label, tone: ok|wait|info|bad}] · noun ["appointment", "appointments"] · bulk [{id, label, variant}]
@@ -37,14 +37,16 @@ export function dataTable(root, cfg, core, on) {
37
37
  };
38
38
  const SORTERS = Object.fromEntries(COLS.map((c) => [c.key, c.type === 'status' ? (r) => ST_ORDER.indexOf(r[c.key]) : c.type === 'date' ? (r) => +asDate(r[c.key]) : (r) => r[c.key]]));
39
39
  const cbHtml = (label, on) => `<button data-slot="checkbox" role="checkbox" data-state="${on ? 'checked' : 'unchecked'}" aria-checked="${on}" class="cn-checkbox" aria-label="${label}"><span data-slot="checkbox-indicator" class="cn-checkbox-indicator"><svg class="cb-mark" viewBox="0 0 24 24">${CB_CHECK}</svg></span></button>`;
40
- const firstSort = dateCol ? { key: dateCol.key, dir: 1 } : { key: COLS[0]?.key, dir: 1 };
40
+ // the table opens sorted by its date column (else the first); a column with sort: "desc" opens newest first (a log)
41
+ const opening = dateCol ?? COLS[0];
42
+ const firstSort = { key: opening?.key, dir: opening?.sort === 'desc' ? -1 : 1 };
41
43
  const bulk = list(cfg.bulk);
42
44
  // rows are selectable only when there is something to do with them: without bulk actions, no checkbox column
43
45
  const picks = bulk.length > 0;
44
46
  // the phone's «Filters» sheet only when there is a filter besides the search
45
47
  const sheetFilters = () => FILTERS.some((f) => f.type !== 'search');
46
48
  const CELL_CLASS = { person: 'cx-dt-who', text: 'cx-dt-svc', date: 'cx-dt-when', status: 'cx-dt-st', money: 'cx-dt-num' };
47
- root.innerHTML = `<div class="cx-dt-host"><div class="cx-dt-toolbar"><div class="cx-hstack cx-dt-tools" role="toolbar" aria-label="${T('Filters')}"></div><span class="cx-text" data-variant="caption" data-dt-shown aria-live="polite"></span></div><div class="cx-hstack cx-dt-chips" hidden></div><div class="cx-dt-bulk" aria-live="polite"><div><span data-dt-count></span><div class="cx-hstack" style="gap:6px">${bulk.map((b) => ui.button({ label: b.label, variant: b.variant || 'outline', size: 'xs', attrs: `data-dt-bulk="${b.id}"` })).join('')}${ui.button({ label: T('Clear selection'), variant: 'ghost', size: 'xs', attrs: 'data-dt-unselect' })}</div></div></div><div class="cx-dt-scroll cx-scroll"><table class="cx-dt"><thead><tr>${picks ? `<th class="cx-dt-check">${cbHtml(T('Select all'), false).replace('class="cn-checkbox"', 'class="cn-checkbox" data-dt-all')}</th>` : ''}${COLS.map((c) => `<th aria-sort="${c.key === firstSort.key ? 'ascending' : 'none'}" ${c.type === 'money' ? 'class="cx-dt-num"' : ''}>${c.sortable === false ? ui.text(c.label, 'label') : `<button class="cn-button cn-button-variant-ghost cn-button-size-xs cx-th" data-sort="${c.key}">${c.label}<svg><use href="#${c.key === firstSort.key ? 'arrowup' : 'sort'}"/></svg></button>`}</th>`).join('')}</tr></thead><tbody></tbody></table></div></div>`;
49
+ root.innerHTML = `<div class="cx-dt-host"><div class="cx-dt-toolbar"><div class="cx-hstack cx-dt-tools" role="toolbar" aria-label="${T('Filters')}"></div><span class="cx-text" data-variant="caption" data-dt-shown aria-live="polite"></span></div><div class="cx-hstack cx-dt-chips" hidden></div><div class="cx-dt-bulk" aria-live="polite"><div><span data-dt-count></span><div class="cx-hstack" style="gap:6px">${bulk.map((b) => ui.button({ label: b.label, variant: b.variant || 'outline', size: 'xs', attrs: `data-dt-bulk="${b.id}"` })).join('')}${ui.button({ label: T('Clear selection'), variant: 'ghost', size: 'xs', attrs: 'data-dt-unselect' })}</div></div></div><div class="cx-dt-scroll cx-scroll"><table class="cx-dt"><thead><tr>${picks ? `<th class="cx-dt-check">${cbHtml(T('Select all'), false).replace('class="cn-checkbox"', 'class="cn-checkbox" data-dt-all')}</th>` : ''}${COLS.map((c) => `<th aria-sort="${c.key === firstSort.key ? (firstSort.dir === 1 ? 'ascending' : 'descending') : 'none'}" ${c.type === 'money' ? 'class="cx-dt-num"' : ''}>${c.sortable === false ? ui.text(c.label, 'label') : `<button class="cn-button cn-button-variant-ghost cn-button-size-xs cx-th" data-sort="${c.key}">${c.label}<svg><use href="#${c.key === firstSort.key ? (firstSort.dir === 1 ? 'arrowup' : 'arrowdown') : 'sort'}"/></svg></button>`}</th>`).join('')}</tr></thead><tbody></tbody></table></div></div>`;
48
50
  const host = $('.cx-dt-host', root), dtTools = $('.cx-dt-tools', root), dtChips = $('.cx-dt-chips', root), dtCountEl = $('[data-dt-shown]', root), dtBody = $('tbody', root), dtAll = $('[data-dt-all]', root), dtBulk = $('.cx-dt-bulk', root);
49
51
  const dtSel = new Set(); let dtSort = firstSort;
50
52
  const passes = (r, except) => FILTERS.every((f) => f.id === except || PASS[f.type](f, fState[f.id], r));