@muten/shadcn 0.2.2 → 0.2.4

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
@@ -370,8 +370,10 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
370
370
  shows nothing: the agenda behind is the context */
371
371
  .cx-modal[data-mode="anchor"] { width: min(24rem, calc(100% - 2rem)); max-height: min(38rem, calc(100dvh - 2rem)); border-radius: calc(var(--radius) * 1.4); overflow: hidden;
372
372
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--foreground) 10%, transparent), 0 24px 60px -20px rgb(20 16 40 / .38);
373
- transform: scale(.96); opacity: 0; transition: transform calc(var(--t-modal) * .5) ease, opacity calc(var(--t-modal) * .4) ease; }
374
- .cx-modal-root[data-open="true"] > .cx-modal[data-mode="anchor"] { transform: none; opacity: 1; transition: transform var(--t-pop, 180ms) var(--ease-out), opacity var(--t-pop, 180ms) var(--ease-out); }
373
+ transform: none; scale: .96; opacity: 0; transition: scale calc(var(--t-modal) * .5) ease, opacity calc(var(--t-modal) * .4) ease; }
374
+ /* it grows in place with `scale`, never `transform`: a modal that was a sheet sits off-screen by its transform, and
375
+ animating that would fly the card in from the side */
376
+ .cx-modal-root[data-open="true"] > .cx-modal[data-mode="anchor"] { scale: 1; opacity: 1; transition: scale var(--t-pop, 180ms) var(--ease-out), opacity var(--t-pop, 180ms) var(--ease-out); }
375
377
  .cx-modal-root:has(> .cx-modal[data-mode="anchor"]) > .cx-overlay { background: transparent; backdrop-filter: none; }
376
378
  /* teléfono = Drawer desde abajo, arrastrable */
377
379
  .cx-modal[data-mode="drawer"] { left: 0; right: 0; bottom: 0; margin-inline: auto; width: min(100%, 34rem); max-height: calc(100dvh - 3rem); border-radius: calc(var(--radius) * 1.8) calc(var(--radius) * 1.8) 0 0; overflow: hidden;
@@ -1494,6 +1496,12 @@ p.cx-text { margin: 0; }
1494
1496
  .cx-thumb[aria-pressed="true"] { background: transparent; box-shadow: 0 0 0 2px var(--selected); }
1495
1497
  .cx-car-card .cn-card-header { padding: 12px 12px 4px; }
1496
1498
  @media (max-width: 640px) { .cx-car { --per: var(--per-sm) !important; } }
1499
+ /* only: "phone": swiped on a phone; on anything wider the same slides stand still in a grid of the wide perView, with
1500
+ no dots and no arrows (data-only is on the root from the first paint, so a wide screen never sees a track) */
1501
+ @media (min-width: 641px) {
1502
+ .cx-car[data-only="phone"] .cx-car-track { grid-auto-flow: row; grid-auto-columns: auto; grid-template-columns: repeat(var(--per, 3), minmax(0, 1fr)); overflow: visible; scroll-snap-type: none; padding-block: 0; margin-block: 0; }
1503
+ .cx-car[data-only="phone"] .cx-car-bar, .cx-car[data-only="phone"] .cx-car-arrow { display: none; }
1504
+ }
1497
1505
 
1498
1506
  /* filters on the phone: search fills the row, «Filtros» beside it; chips scroll sideways */
1499
1507
  .cx-dt-host:has(.cx-fbtn-m) .cx-dt-tools { width: 100%; flex-wrap: nowrap; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muten/shadcn",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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",
@@ -72,9 +72,17 @@ export function mount(el, inputs, on) {
72
72
  };
73
73
  map = data;
74
74
  const send = (name, value) => on[name]?.(typeof value === 'string' ? value : JSON.stringify(value));
75
- // WHERE A CITA IS ON SCREEN ("x,y,w,h"), so the app can open it beside itself (Modal mode "anchor"): the shown chip
76
- // or row that carries its id ('' when none is visible, e.g. opened from a command)
77
- const whereIs = (id) => { const box = [...el.querySelectorAll(`[data-ev="${CSS.escape(String(id))}"]`)].map((x) => x.getBoundingClientRect()).find((r) => r.width > 0 && r.height > 0); return box ? [box.left, box.top, box.width, box.height].map(Math.round).join(',') : ''; };
75
+ // WHERE A CITA IS ON SCREEN ("x,y,w,h"), so the app can open it beside itself (Modal mode "anchor"): the very chip,
76
+ // or row that was tapped; else its chip or row (never the «now» card: a band as wide as the agenda, nothing to sit by);
77
+ // '' when none is visible (opened from a command)
78
+ let tapped = null;
79
+ el.addEventListener('click', (e) => { tapped = e.target.closest?.('[data-ev]') ?? null; }, true);
80
+ const boxOf = (x) => { const r = x?.getBoundingClientRect(); return r && r.width > 0 && r.height > 0 ? [r.left, r.top, r.width, r.height].map(Math.round).join(',') : ''; };
81
+ const whereIs = (id) => {
82
+ if (tapped?.isConnected && !tapped.matches('.cx-ag-nowcard') && tapped.dataset.ev === String(id) && boxOf(tapped)) return boxOf(tapped);
83
+ const own = [...el.querySelectorAll(`.cx-ag-ev[data-ev="${CSS.escape(String(id))}"], .cx-ag-row[data-ev="${CSS.escape(String(id))}"]`)].map(boxOf).find(Boolean);
84
+ return own ?? '';
85
+ };
78
86
  const day1 = (x) => ({ ...x, day: x.day instanceof Date ? iso(x.day) : x.day });
79
87
  el.innerHTML = '';
80
88
  api = kit.agenda(el, {
@@ -1,11 +1,12 @@
1
1
  # Carousel - one component, options instead of copies. The slides are your muten content (CarouselSlide).
2
2
  # perView "4" or "2,4" (phone, wider) · center "on" (the active one centred, neighbours peeking) · loop "on" ·
3
3
  # autoplay ms (pauses on hover, focus, touch, hidden tab; off with reduced motion; its own pause button) ·
4
- # indicator dots | progress | count | thumbs · arrows bar | overlay
4
+ # indicator dots | progress | count | thumbs · arrows bar | overlay | none ·
5
+ # only "phone" (a carousel on a phone; on a wider screen the same slides sit still in a grid of perView's wide count)
5
6
  # Carousel(label: "Products", perView: "2,4") { each products as p { CarouselSlide { … } } }
6
7
  # shadcn-for-muten · part + Custom (CarouselBehavior, kit collections) · kit.css (.cx-car).
7
- part Carousel(label: text, perView: text = "1", center: text = "", loop: text = "", autoplay: number = 0, indicator: text = "dots", arrows: text = "bar") {
8
- Section class("cx-car") aria(label: $label, roledescription: "carrusel") {
8
+ part Carousel(label: text, perView: text = "1", center: text = "", loop: text = "", autoplay: number = 0, indicator: text = "dots", arrows: text = "bar", only: text = "") {
9
+ Section class("cx-car") data(only: $only) aria(label: $label, roledescription: "carrusel") {
9
10
  Stack class("cx-car-view") { Stack class("cx-car-track") { slot } }
10
11
  Custom Carousel inputs(perView: $perView, center: $center, loop: $loop, autoplay: $autoplay, indicator: $indicator, arrows: $arrows) class("cx-car-behavior")
11
12
  }
@@ -21,9 +21,11 @@ export function mount(el, inputs, on) {
21
21
  let anchor = String(inputs.anchor || '');
22
22
  // `want` is the mode the page asked for; `mode` is what it is now (an anchor with nothing to hold on to is a dialog)
23
23
  const wantsAnchor = () => mdl.dataset.want === 'anchor';
24
+ // what an earlier opening as a card left on the element: a sheet or a dialog opened next must not keep it
25
+ const unplace = () => { mdl.style.left = ''; mdl.style.top = ''; mdl.style.transformOrigin = ''; };
24
26
  const place = () => {
25
27
  const at = anchor.split(',').map(Number);
26
- if (!wantsAnchor() || phone.matches || at.length !== 4 || at.some((n) => !Number.isFinite(n))) { if (wantsAnchor()) mdl.dataset.mode = phone.matches ? 'drawer' : 'dialog'; return; }
28
+ if (!wantsAnchor() || phone.matches || at.length !== 4 || at.some((n) => !Number.isFinite(n))) { unplace(); if (wantsAnchor()) mdl.dataset.mode = phone.matches ? 'drawer' : 'dialog'; return; }
27
29
  mdl.dataset.mode = 'anchor';
28
30
  const [x, y, w] = at, gap = 12, edge = 16;
29
31
  const width = mdl.offsetWidth, height = Math.min(mdl.scrollHeight, innerHeight - edge * 2);
@@ -36,7 +38,7 @@ export function mount(el, inputs, on) {
36
38
  const open = () => {
37
39
  if (isOpen) return; isOpen = true; placeFooter();
38
40
  if (auto) mdl.dataset.mode = phone.matches ? 'drawer' : 'dialog';
39
- if (wantsAnchor()) place();
41
+ if (wantsAnchor()) place(); else unplace();
40
42
  lastFocus = document.activeElement;
41
43
  document.documentElement.classList.add('cx-locked');
42
44
  // on the phone, focusing a field would throw the keyboard over the sheet: focus the sheet itself