@muten/shadcn 0.2.15 → 0.2.17

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
@@ -5639,3 +5639,13 @@ a.cx-bc-item:hover { color: var(--foreground); }
5639
5639
  .cx-sidebar[data-collapsed="true"] .cx-sb-fold > .cx-sb-fold-in { visibility: visible; }
5640
5640
  .cx-sidebar[data-collapsed="true"] :is(.cx-sb-rowtoggle, .cx-sb-chev) { display: none; }
5641
5641
  @media (prefers-reduced-motion: reduce) { .cx-sb-fold, .cx-sb-chev { transition: none; } }
5642
+
5643
+ /* ═════ EVERYTHING YOU CAN CLICK SHOWS THE HAND ═════ */
5644
+ /* A browser gives a <button> the arrow, and shadcn gives menu items, options and selects `cursor: default` (the desktop-app
5645
+ convention). On the web the hand is what says «this does something», so every enabled control takes it: buttons,
5646
+ tabs, menu items, options, switches, checkboxes, toggles, triggers, labels of a box. Disabled ones keep the arrow, and a
5647
+ modal's backdrop (a button that fills the screen) too. The :is() weighs as an element plus an attribute, so it wins
5648
+ over the single-class `cursor: default` rules above. */
5649
+ :is(button, a[href], summary, select, label[for], input[type="checkbox"], input[type="radio"], input[type="range"], input[type="color"], input[type="file"],
5650
+ [role="button"], [role="tab"], [role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"], [role="option"], [role="switch"], [role="checkbox"], [role="radio"], [data-slot$="trigger"]):not(:disabled, [aria-disabled="true"], [data-disabled], .cx-overlay) { cursor: pointer; }
5651
+ :is(button, [role="button"], [role="menuitem"], [role="option"], [role="switch"], [role="tab"]):is(:disabled, [aria-disabled="true"], [data-disabled]) { cursor: not-allowed; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muten/shadcn",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
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",
@@ -5,12 +5,17 @@
5
5
  // MODALS STACK (a receipt opened over a cita): Back, Esc and the focus trap belong to the TOP one only, and the page's
6
6
  // scroll is unlocked when the last one closes. Each modal used to listen to every Back: closing the receipt went back
7
7
  // in history and that Back also closed the cita under it, leaving the agenda without its buttons (21-10-2026).
8
- const stack = (globalThis.__cxModals ||= { list: [], skip: 0, wired: false });
8
+ // ONE CLOSES AS THE NEXT OPENS («Añadir» → the link's editor): the closing one's history.back() lands after the new one's
9
+ // pushState, and Chrome counts that Back from where it was asked, so it stepped over the new entry. The new modal then
10
+ // closed with a Back that was not its own and took the page one screen back (/shop/import, 31-10-2026). So a modal that
11
+ // opens while a closing Back is still on its way pushes its entry once that Back has landed (`after`).
12
+ const stack = (globalThis.__cxModals ||= { list: [], skip: 0, after: [], wired: false });
13
+ stack.after ||= [];
9
14
  if (!stack.wired) {
10
15
  stack.wired = true;
11
16
  addEventListener('popstate', () => {
12
17
  // the history.back() a modal makes when it closes itself is its own, not a Back for the one under it
13
- if (stack.skip > 0) { stack.skip -= 1; return; }
18
+ if (stack.skip > 0) { stack.skip -= 1; if (stack.skip === 0) stack.after.splice(0).forEach((push) => push()); return; }
14
19
  stack.list.at(-1)?.onPop();
15
20
  });
16
21
  }
@@ -59,7 +64,8 @@ export function mount(el, inputs, on) {
59
64
  document.documentElement.classList.add('cx-locked');
60
65
  // on the phone, focusing a field would throw the keyboard over the sheet: focus the sheet itself
61
66
  requestAnimationFrame(() => { ((mdl.dataset.mode === 'dialog' && focusables().find((x) => x.matches('input, textarea'))) || mdl).focus({ preventScroll: true }); });
62
- history.pushState({ cxModal: 1 }, ''); pushed = true;
67
+ const push = () => { if (!isOpen || pushed) return; history.pushState({ cxModal: 1 }, ''); pushed = true; };
68
+ if (stack.skip > 0) stack.after.push(push); else push();
63
69
  };
64
70
  const shut = () => {
65
71
  if (!isOpen) return; isOpen = false;