@muten/shadcn 0.1.11 → 0.1.12
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 +22 -2
- package/package.json +1 -1
- package/registry/kit/core.js +30 -3
- package/registry.json +8 -8
package/kit.css
CHANGED
|
@@ -432,8 +432,19 @@ html.cx-locked { overflow: hidden; }
|
|
|
432
432
|
.cx-who b { display: block; font-weight: 500; }
|
|
433
433
|
.cx-who small { color: var(--muted-foreground); font-size: 13px; }
|
|
434
434
|
.cx-online { background: var(--success) !important; box-shadow: 0 0 0 2px var(--background); }
|
|
435
|
-
.cx-tip { position: fixed; z-index: var(--z-toast); padding: 6px
|
|
435
|
+
.cx-tip { position: fixed; z-index: var(--z-toast); padding: 6px 12px; border-radius: calc(var(--radius) - 2px); background: var(--foreground); color: var(--background); font-size: 12px; font-weight: 500; line-height: 1.35; white-space: nowrap; pointer-events: none; opacity: 0; transform: translateY(3px) scale(.96); transform-origin: var(--ax, 50%) 100%; transition: opacity 120ms ease-out, transform 160ms cubic-bezier(.23, 1, .32, 1); }
|
|
436
|
+
/* its arrow, toward the trigger (shadcn's TooltipArrow: the pill's own colour, a softly rounded square half out) */
|
|
437
|
+
.cx-tip::after { content: ""; position: absolute; left: calc(var(--ax, 50%) - 5px); bottom: -4px; width: 10px; height: 10px; border-radius: 2px; background: inherit; transform: rotate(45deg); }
|
|
438
|
+
.cx-tip[data-side="bottom"] { transform: translateY(-3px) scale(.96); transform-origin: var(--ax, 50%) 0; }
|
|
439
|
+
.cx-tip[data-side="bottom"]::after { bottom: auto; top: -4px; }
|
|
440
|
+
.cx-tip[data-side="right"] { transform: translateX(-3px) scale(.96); transform-origin: 0 50%; }
|
|
441
|
+
.cx-tip[data-side="right"]::after { left: -4px; top: calc(50% - 5px); bottom: auto; }
|
|
436
442
|
.cx-tip[data-state="open"] { opacity: 1; transform: none; }
|
|
443
|
+
@media (prefers-reduced-motion: reduce) { .cx-tip { transition: opacity 120ms ease-out; transform: none; } }
|
|
444
|
+
.cx-tip[data-side="right"] { transform: translateX(-4px); }
|
|
445
|
+
.cx-tip[data-side="right"][data-state="open"] { transform: none; }
|
|
446
|
+
/* after the first one, the names follow the pointer at once: no fade, no slide */
|
|
447
|
+
.cx-tip[data-instant] { transition-duration: 0ms; }
|
|
437
448
|
|
|
438
449
|
/* ── Fields ── */
|
|
439
450
|
.cx-form { display: flex; flex-direction: column; gap: 18px; width: 100%; max-width: 380px; }
|
|
@@ -4941,6 +4952,15 @@ a.cx-bc-item:hover { color: var(--foreground); }
|
|
|
4941
4952
|
.cx-sb-label { white-space: nowrap; overflow: hidden; }
|
|
4942
4953
|
.cx-sidebar[data-collapsed="true"] .cx-sb-label, .cx-sidebar[data-collapsed="true"] .cx-sb-glabel { display: block; opacity: 0; pointer-events: none; }
|
|
4943
4954
|
.cx-sidebar[data-collapsed="true"] .cx-sb-count:not(:empty) { display: inline-grid; opacity: 0; }
|
|
4955
|
+
/* FOLDED, A SECTION'S TITLE BECOMES A SHORT LINE between its icons and the ones above: its words fade where they are and
|
|
4956
|
+
its height closes with the fold (200 ms), so the icon column has no holes where the titles were. A group with no
|
|
4957
|
+
title (the first) takes no room at all. */
|
|
4958
|
+
.cx-sidebar .cx-sb-glabel { position: relative; transition: opacity .16s ease, height .2s cubic-bezier(.22, 1, .36, 1), padding .2s cubic-bezier(.22, 1, .36, 1); }
|
|
4959
|
+
.cx-sidebar[data-collapsed="true"] .cx-sb-glabel { height: 17px; padding-top: 0; padding-bottom: 0; overflow: hidden; color: transparent; opacity: 1; }
|
|
4960
|
+
.cx-sidebar[data-collapsed="true"] .cx-sb-glabel::after { content: ""; position: absolute; left: 50%; top: 8px; width: 20px; height: 1px; margin-left: -10px; background: var(--border); }
|
|
4961
|
+
.cx-sidebar[data-collapsed="true"] .cx-sb-glabel:empty { display: none; }
|
|
4962
|
+
.cx-sidebar .cx-sb-group { transition: padding .2s cubic-bezier(.22, 1, .36, 1); }
|
|
4963
|
+
.cx-sidebar[data-collapsed="true"] .cx-sb-group { padding-top: 2px; padding-bottom: 2px; }
|
|
4944
4964
|
/* A SECTION THAT LOADS: while its page is on its way (the router marks the link with data-loading), the row's icon
|
|
4945
4965
|
turns into a small ring that spins, and turns back when the page is there. */
|
|
4946
4966
|
@keyframes cx-sb-spin { to { transform: rotate(360deg); } }
|
|
@@ -4948,7 +4968,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
|
|
|
4948
4968
|
.cx-sb-item[data-loading] > .mu-icon { opacity: 0; transform: scale(.4) rotate(-90deg); }
|
|
4949
4969
|
.cx-sb-item[data-loading]::before { content: ""; position: absolute; left: var(--sb-pad, 8px); top: 50%; width: 16px; height: 16px; margin-top: -8px; box-sizing: border-box; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: cx-sb-spin .7s linear infinite; }
|
|
4950
4970
|
@media (prefers-reduced-motion: reduce) {
|
|
4951
|
-
.cx-sidebar .cx-sb-item, .cx-sb-label, .cx-sb-glabel, .cx-sb-count, .cx-sb-item > .mu-icon { transition: none; }
|
|
4971
|
+
.cx-sidebar .cx-sb-item, .cx-sb-label, .cx-sb-glabel, .cx-sidebar .cx-sb-glabel, .cx-sb-count, .cx-sb-item > .mu-icon, .cx-tip { transition: none; }
|
|
4952
4972
|
.cx-sb-item[data-loading]::before { animation-duration: 1.6s; }
|
|
4953
4973
|
}
|
|
4954
4974
|
/* THE MONOGRAM: a soft tile in the name's own hue, the letters in a deep shade of the same hue, a hairline ring of it.
|
package/package.json
CHANGED
package/registry/kit/core.js
CHANGED
|
@@ -182,10 +182,37 @@ watchReveal();
|
|
|
182
182
|
};
|
|
183
183
|
|
|
184
184
|
// ── tooltip on any [data-name] ──
|
|
185
|
+
// shadcn's: the foreground pill with its arrow, above the trigger (beside it in a rail, below when there is no room).
|
|
186
|
+
// It holds while the pointer travels inside its trigger (icon to label is not leaving, so it never blinks), it goes on
|
|
187
|
+
// a press, and in a sidebar it only speaks while the rail is folded to icons: open, the label is already on screen.
|
|
185
188
|
const tip = document.createElement('div'); tip.className = 'cx-tip'; tip.setAttribute('role', 'tooltip'); tip.hidden = true; lib.appendChild(tip);
|
|
186
|
-
|
|
187
|
-
const
|
|
189
|
+
let tipFor = null;
|
|
190
|
+
const tipHide = () => { tipFor = null; tip.dataset.state = 'closed'; tip.hidden = true; };
|
|
191
|
+
const tipOn = (e) => {
|
|
192
|
+
const w = e.target.closest?.('[data-name]');
|
|
193
|
+
if (!w || !w.dataset.name || w === tipFor) return; /* an empty name (no data yet) says nothing */
|
|
194
|
+
const rail = w.closest('[data-slot="sidebar"]');
|
|
195
|
+
if (rail && rail.dataset.collapsed !== 'true' && w.dataset.slot !== 'tooltip-trigger') return;
|
|
196
|
+
tipFor = w; tip.textContent = w.dataset.name; tip.hidden = false; tip.dataset.state = 'closed';
|
|
197
|
+
const r = w.getBoundingClientRect();
|
|
198
|
+
if (w.dataset.side === 'right' || rail) {
|
|
199
|
+
tip.dataset.side = 'right'; tip.style.left = (r.right + 8) + 'px'; tip.style.top = (r.top + r.height / 2 - tip.offsetHeight / 2) + 'px';
|
|
200
|
+
} else {
|
|
201
|
+
place(tip, w, 'center', 8);
|
|
202
|
+
const above = r.top - tip.offsetHeight - 8 > 8;
|
|
203
|
+
tip.dataset.side = above ? 'top' : 'bottom';
|
|
204
|
+
tip.style.top = (above ? r.top - tip.offsetHeight - 8 : r.bottom + 8) + 'px';
|
|
205
|
+
tip.style.setProperty('--ax', (r.left + r.width / 2 - parseFloat(tip.style.left)) + 'px');
|
|
206
|
+
}
|
|
207
|
+
requestAnimationFrame(() => { if (tipFor === w) tip.dataset.state = 'open'; });
|
|
208
|
+
};
|
|
209
|
+
const tipOff = (e) => {
|
|
210
|
+
const w = e.target.closest?.('[data-name]');
|
|
211
|
+
if (!w || w !== tipFor || (e.relatedTarget && w.contains(e.relatedTarget))) return;
|
|
212
|
+
tipHide();
|
|
213
|
+
};
|
|
188
214
|
['pointerover', 'focusin'].forEach((t) => document.addEventListener(t, tipOn)); ['pointerout', 'focusout'].forEach((t) => document.addEventListener(t, tipOff));
|
|
215
|
+
document.addEventListener('pointerdown', () => { if (tipFor) tipHide(); }, true);
|
|
189
216
|
|
|
190
217
|
// ── island: one floating notice for the whole app; never stacks, morphs its width to the next text ──
|
|
191
218
|
const isl = document.createElement('div'); isl.className = 'cx-island cx-island-app'; isl.setAttribute('aria-live', 'polite'); isl.dataset.state = 'hidden';
|
|
@@ -1324,7 +1351,7 @@ let wheelsNow = [];
|
|
|
1324
1351
|
if (periods) { const tl = $('.cx-tabs', root); initTabs(tl); tl.addEventListener('pick', (e) => { const d = periods[e.detail][1]; paint(d); const t2 = $$('.cx-tab', root); t2.forEach((x, i) => x.setAttribute('aria-selected', i === e.detail)); }); }
|
|
1325
1352
|
$$('.cx-chart-bar', root).forEach((b, i) => b.animate([{ transform: 'scaleY(0)' }, { transform: 'scaleY(1)' }], { duration: 420, delay: i * 18, easing: 'cubic-bezier(.22,1,.36,1)', fill: 'backwards' }));
|
|
1326
1353
|
};
|
|
1327
|
-
root.addEventListener('pointerover', (e) => { const b = e.target.closest('.cx-chart-bar'); if (b) { tip.textContent = b.dataset.tipV; tip.hidden = false; const r = b.getBoundingClientRect(); tip.style.left = (r.left + r.width / 2 - tip.offsetWidth / 2) + 'px'; tip.style.top = (r.top + (1 - parseFloat(getComputedStyle(b).getPropertyValue('--v')) / 100) * r.height - tip.offsetHeight - 6) + 'px'; requestAnimationFrame(() => tip.dataset.state = 'open'); } });
|
|
1354
|
+
root.addEventListener('pointerover', (e) => { const b = e.target.closest('.cx-chart-bar'); if (b) { tip.textContent = b.dataset.tipV; tip.hidden = false; tip.dataset.side = 'top'; tip.style.removeProperty('--ax'); const r = b.getBoundingClientRect(); tip.style.left = (r.left + r.width / 2 - tip.offsetWidth / 2) + 'px'; tip.style.top = (r.top + (1 - parseFloat(getComputedStyle(b).getPropertyValue('--v')) / 100) * r.height - tip.offsetHeight - 6) + 'px'; requestAnimationFrame(() => tip.dataset.state = 'open'); } });
|
|
1328
1355
|
root.addEventListener('pointerout', (e) => { if (e.target.closest('.cx-chart-bar')) { tip.dataset.state = 'closed'; tip.hidden = true; } });
|
|
1329
1356
|
root.addEventListener('focusin', (e) => e.target.closest('.cx-chart-bar')?.dispatchEvent(new PointerEvent('pointerover', { bubbles: true })));
|
|
1330
1357
|
paint(data);
|
package/registry.json
CHANGED
|
@@ -579,6 +579,14 @@
|
|
|
579
579
|
"component": "Mascot",
|
|
580
580
|
"description": "Mascot - the brand's mascot (in Storio, Storio): an AssistantOrb that acts. Its gestures are hand-set keyframes that"
|
|
581
581
|
},
|
|
582
|
+
{
|
|
583
|
+
"name": "media-query",
|
|
584
|
+
"deps": [],
|
|
585
|
+
"part": "MediaQuery",
|
|
586
|
+
"file": "registry/media-query.muten",
|
|
587
|
+
"component": "MediaQuery",
|
|
588
|
+
"description": "MediaQuery(query, onChange): says \"yes\"/\"no\" for a CSS media query at mount and on every change, so a page draws one version (phone or desktop) instead of both."
|
|
589
|
+
},
|
|
582
590
|
{
|
|
583
591
|
"name": "member-list",
|
|
584
592
|
"deps": [
|
|
@@ -1198,14 +1206,6 @@
|
|
|
1198
1206
|
"component": "VirtualList",
|
|
1199
1207
|
"description": "VirtualList(rows, total, rowHeight, label, jump): only visible rows in the DOM."
|
|
1200
1208
|
},
|
|
1201
|
-
{
|
|
1202
|
-
"name": "media-query",
|
|
1203
|
-
"deps": [],
|
|
1204
|
-
"part": "MediaQuery",
|
|
1205
|
-
"file": "registry/media-query.muten",
|
|
1206
|
-
"component": "MediaQuery",
|
|
1207
|
-
"description": "MediaQuery(query, onChange): says \"yes\"/\"no\" for a CSS media query at mount and on every change, so a page draws one version (phone or desktop) instead of both."
|
|
1208
|
-
},
|
|
1209
1209
|
{
|
|
1210
1210
|
"name": "virtual-window",
|
|
1211
1211
|
"deps": [],
|