@filipgutica/ui 1.9.0 → 1.10.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.
- package/README.md +16 -9
- package/dist/index.js +1 -1
- package/dist/site.js +1 -1
- package/dist/{use-active-section-qEJQS1S9.js → use-active-section-DCS_wyYq.js} +59 -21
- package/package.json +1 -1
- package/src/components/tabs.css +9 -0
- package/src/site.css +21 -2
- package/src/themes/graphite-indigo.css +1 -1
package/README.md
CHANGED
|
@@ -50,9 +50,10 @@ cancelable `closeAutoFocus` event and focus that heading.
|
|
|
50
50
|
|
|
51
51
|
`UiTabs` renders every labelled panel during SSR and initial hydration. After
|
|
52
52
|
mounting, it adds keyboard-accessible tabs and hides inactive panels without
|
|
53
|
-
unmounting them.
|
|
54
|
-
the
|
|
55
|
-
|
|
53
|
+
unmounting them. A tab strip narrower than its tabs scrolls, fades each clipped
|
|
54
|
+
edge, and keeps the selected tab in view when it resizes. Use unique item values
|
|
55
|
+
and keep page anchor headings outside the panels. Re-align an initial hash after
|
|
56
|
+
layout settles if enhancement changes the page height.
|
|
56
57
|
|
|
57
58
|
Track page sections from a component's setup function:
|
|
58
59
|
|
|
@@ -68,7 +69,10 @@ The returned readonly ref contains the last supplied heading above the root's
|
|
|
68
69
|
CSS `scroll-padding-top` inset, or the final section at the document bottom.
|
|
69
70
|
IDs must remain stable and in page order; missing elements are ignored after
|
|
70
71
|
mounting. Bind matching links to `aria-current="location"`. Scrolling, resizing,
|
|
71
|
-
layout changes, completed transitions, and restored pages update the value.
|
|
72
|
+
layout changes, completed transitions, and restored pages update the value. After
|
|
73
|
+
a jump to a section, through a `#id` link, a hash change, or a deep link, that
|
|
74
|
+
section stays current until the reader scrolls away from where the jump lands, so
|
|
75
|
+
a page too short to bring it to the top still marks it.
|
|
72
76
|
The composable does not change focus, the URL, or browser history, and removes
|
|
73
77
|
its listeners and observer when the component unmounts. During SSR it returns
|
|
74
78
|
the first supplied ID without accessing browser globals.
|
|
@@ -198,9 +202,10 @@ description and wraps.
|
|
|
198
202
|
|
|
199
203
|
`site.css` also includes page helpers: `.fg-site-steps` for an ordered list with
|
|
200
204
|
numbered circles, `.fg-site-facts` for an inline `dl` of short label and value
|
|
201
|
-
pairs
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
pairs whose links get a `--control-lg` hit area at 640px and below,
|
|
206
|
+
`.fg-site-definitions` for a two-column `dl` of `dt` and `dd` that stacks on
|
|
207
|
+
phones and keeps `code` such as `--all` on one line, `.fg-site-sr-only` for
|
|
208
|
+
visually hidden text, and `.fg-site-skip` for a skip link that appears on focus.
|
|
204
209
|
|
|
205
210
|
### Docs shell
|
|
206
211
|
|
|
@@ -211,9 +216,11 @@ main content, and footer. The shell then supplies:
|
|
|
211
216
|
the footer on the frame edges. At 800px and below the rail stacks above `main`.
|
|
212
217
|
- element defaults inside the shell, plus the page background and font on
|
|
213
218
|
`body`: box sizing, heading sizes and margins, paragraph measure, links,
|
|
214
|
-
`kbd`,
|
|
219
|
+
inline `code` at `--text-sm`, `kbd`, a 2px focus outline, and a `[hidden]`
|
|
220
|
+
rule that outranks the page's own `display` rules.
|
|
215
221
|
- `.fg-site-footer` for the footer and `.fg-site-legend` for a `dl` that explains
|
|
216
|
-
what a capture shows.
|
|
222
|
+
what a capture shows. Legend columns are at least `--site-legend-column` wide,
|
|
223
|
+
`--sidebar` by default; set it on the legend to fit more entries in a row.
|
|
217
224
|
|
|
218
225
|
```vue
|
|
219
226
|
<template>
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-DCS_wyYq.js";
|
|
2
2
|
import { Fragment as o, computed as s, createBlock as c, createCommentVNode as l, createElementBlock as u, createElementVNode as d, createTextVNode as f, createVNode as p, defineComponent as m, inject as h, mergeModels as g, mergeProps as _, normalizeStyle as v, openBlock as y, provide as b, ref as ee, renderList as x, renderSlot as S, resolveDynamicComponent as C, shallowRef as te, toDisplayString as w, unref as T, useId as E, useModel as ne, useSlots as D, watch as re, withCtx as O } from "vue";
|
|
3
3
|
import { CheckboxIndicator as k, CheckboxRoot as A, Primitive as j, ProgressIndicator as M, ProgressRoot as N, RadioGroupItem as P, RadioGroupRoot as F, SliderRange as I, SliderRoot as L, SliderThumb as R, SliderTrack as z, Toggle as B } from "reka-ui";
|
|
4
4
|
//#region src/components/UiCard.vue?vue&type=script&setup=true&lang.ts
|
package/dist/site.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-DCS_wyYq.js";
|
|
2
2
|
import { Fragment as o, computed as s, createBlock as c, createCommentVNode as l, createElementBlock as u, createElementVNode as d, createTextVNode as f, createVNode as p, defineComponent as m, mergeModels as h, nextTick as g, normalizeClass as _, normalizeStyle as v, onBeforeUnmount as y, onMounted as b, onUnmounted as x, openBlock as S, ref as C, renderList as w, renderSlot as T, shallowRef as E, toDisplayString as D, unref as O, useId as k, useModel as ee, useSlots as A, useTemplateRef as te, watch as j, withCtx as M } from "vue";
|
|
3
3
|
import { tryOnMounted as N, usePreferredDark as P, useStorage as F } from "@vueuse/core";
|
|
4
4
|
//#region src/site/appearance.ts
|
|
@@ -154,7 +154,7 @@ var N = /* @__PURE__ */ c({
|
|
|
154
154
|
_: 3
|
|
155
155
|
}, 8, ["open"]));
|
|
156
156
|
}
|
|
157
|
-
}), H = { class: "fg-tabs" }, U = ["aria-label"], W = [
|
|
157
|
+
}), H = { class: "fg-tabs" }, U = ["aria-label", "data-clipped"], W = [
|
|
158
158
|
"id",
|
|
159
159
|
"aria-controls",
|
|
160
160
|
"aria-selected",
|
|
@@ -180,16 +180,34 @@ var N = /* @__PURE__ */ c({
|
|
|
180
180
|
u(() => {
|
|
181
181
|
l.value = !0;
|
|
182
182
|
});
|
|
183
|
-
let C = b("list")
|
|
184
|
-
x([l, d], () => {
|
|
183
|
+
let C = b("list"), w = () => {
|
|
185
184
|
let e = C.value, t = e?.querySelector("[aria-selected=\"true\"]");
|
|
186
185
|
if (!e || !t) return;
|
|
187
|
-
let n = e.getBoundingClientRect(), { left:
|
|
188
|
-
|
|
189
|
-
},
|
|
190
|
-
|
|
186
|
+
let n = e.getBoundingClientRect(), r = getComputedStyle(e), i = n.left + (Number.parseFloat(r.scrollPaddingLeft) || 0), a = n.right - (Number.parseFloat(r.scrollPaddingRight) || 0), { left: o, right: s } = t.getBoundingClientRect();
|
|
187
|
+
o < i ? e.scrollLeft -= i - o : s > a && (e.scrollLeft += s - a);
|
|
188
|
+
}, T = m(), E = () => {
|
|
189
|
+
let e = C.value;
|
|
190
|
+
if (!e) return;
|
|
191
|
+
let t = e.scrollWidth - e.clientWidth, n = getComputedStyle(e).direction === "rtl" ? e.scrollLeft + t : e.scrollLeft, r = n > 1, i = n < t - 1;
|
|
192
|
+
T.value = r && i ? "both" : r ? "left" : i ? "right" : void 0;
|
|
193
|
+
}, D = () => {
|
|
194
|
+
w(), E();
|
|
195
|
+
};
|
|
196
|
+
x([
|
|
197
|
+
l,
|
|
198
|
+
d,
|
|
199
|
+
() => n.items
|
|
200
|
+
], D, {
|
|
201
|
+
flush: "post",
|
|
202
|
+
deep: !0
|
|
203
|
+
}), x(C, (e, t, n) => {
|
|
204
|
+
if (!e || typeof ResizeObserver > "u") return;
|
|
205
|
+
let r = new ResizeObserver(D);
|
|
206
|
+
r.observe(e), n(() => r.disconnect());
|
|
207
|
+
});
|
|
208
|
+
let O = (e) => {
|
|
191
209
|
e !== d.value && s("update:modelValue", e);
|
|
192
|
-
},
|
|
210
|
+
}, k = (e) => {
|
|
193
211
|
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || ![
|
|
194
212
|
"ArrowLeft",
|
|
195
213
|
"ArrowRight",
|
|
@@ -213,7 +231,9 @@ var N = /* @__PURE__ */ c({
|
|
|
213
231
|
class: "fg-tabs__list",
|
|
214
232
|
role: "tablist",
|
|
215
233
|
"aria-label": n.label,
|
|
216
|
-
|
|
234
|
+
"data-clipped": T.value,
|
|
235
|
+
onKeydown: k,
|
|
236
|
+
onScrollPassive: E
|
|
217
237
|
}, [(f(!0), i(e, null, h(n.items, (e) => (f(), i("button", {
|
|
218
238
|
id: p(e.value),
|
|
219
239
|
key: e.value,
|
|
@@ -223,8 +243,8 @@ var N = /* @__PURE__ */ c({
|
|
|
223
243
|
"aria-controls": v(e.value),
|
|
224
244
|
"aria-selected": e.value === d.value,
|
|
225
245
|
tabindex: e.value === d.value ? 0 : -1,
|
|
226
|
-
onClick: (t) =>
|
|
227
|
-
onFocus: (t) =>
|
|
246
|
+
onClick: (t) => O(e.value),
|
|
247
|
+
onFocus: (t) => O(e.value)
|
|
228
248
|
}, _(e.label), 41, W))), 128))], 40, U)) : r("", !0), (f(!0), i(e, null, h(n.items, (e) => (f(), i("section", {
|
|
229
249
|
id: v(e.value),
|
|
230
250
|
key: e.value,
|
|
@@ -248,17 +268,35 @@ var N = /* @__PURE__ */ c({
|
|
|
248
268
|
id: e,
|
|
249
269
|
element: t
|
|
250
270
|
}] : [];
|
|
251
|
-
}), o = () => {
|
|
252
|
-
n = void 0
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
271
|
+
}), o = document.documentElement, s = () => Number.parseFloat(getComputedStyle(o).scrollPaddingTop) || 0, c, l = () => {
|
|
272
|
+
if (n = void 0, c) {
|
|
273
|
+
let e = Math.abs(window.scrollY - c.landing);
|
|
274
|
+
e > c.closest + 2 ? c = void 0 : c.closest = Math.min(c.closest, e);
|
|
275
|
+
}
|
|
276
|
+
let e = s(), r = a[0]?.id;
|
|
277
|
+
for (let t of a) t.element.getBoundingClientRect().top <= e + 1 && (r = t.id);
|
|
278
|
+
window.scrollY > 0 && window.scrollY + window.innerHeight >= o.scrollHeight - 2 && (r = a.at(-1)?.id), t.value = c?.id ?? r;
|
|
279
|
+
}, u = () => {
|
|
280
|
+
n === void 0 && (n = requestAnimationFrame(l));
|
|
281
|
+
}, d = (e) => {
|
|
282
|
+
let t = a.find((t) => t.id === e);
|
|
283
|
+
if (c = void 0, t) {
|
|
284
|
+
let n = Math.max(0, o.scrollHeight - window.innerHeight), r = window.scrollY + t.element.getBoundingClientRect().top - s(), i = Math.min(Math.max(r, 0), n);
|
|
285
|
+
c = {
|
|
286
|
+
id: e,
|
|
287
|
+
landing: i,
|
|
288
|
+
closest: Math.abs(window.scrollY - i)
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
u();
|
|
292
|
+
}, f = () => d(location.hash.slice(1)), p = (e) => {
|
|
293
|
+
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
294
|
+
let t = e.target instanceof Element ? e.target.closest("a[href^=\"#\"]")?.getAttribute("href") : void 0;
|
|
295
|
+
t && d(t.slice(1));
|
|
258
296
|
};
|
|
259
|
-
window.addEventListener("scroll",
|
|
260
|
-
window.removeEventListener("scroll",
|
|
261
|
-
},
|
|
297
|
+
window.addEventListener("scroll", u, { passive: !0 }), window.addEventListener("resize", u), window.addEventListener("pageshow", u), window.addEventListener("hashchange", f), document.addEventListener("click", p), document.body.addEventListener("transitionend", u), typeof ResizeObserver < "u" && (r = new ResizeObserver(u), r.observe(document.body)), i = () => {
|
|
298
|
+
window.removeEventListener("scroll", u), window.removeEventListener("resize", u), window.removeEventListener("pageshow", u), window.removeEventListener("hashchange", f), document.removeEventListener("click", p), document.body.removeEventListener("transitionend", u);
|
|
299
|
+
}, location.hash && f(), l();
|
|
262
300
|
}), d(() => {
|
|
263
301
|
i?.(), r?.disconnect(), n !== void 0 && cancelAnimationFrame(n);
|
|
264
302
|
}), p(t);
|
package/package.json
CHANGED
package/src/components/tabs.css
CHANGED
|
@@ -12,10 +12,19 @@
|
|
|
12
12
|
width: max-content;
|
|
13
13
|
max-width: 100%;
|
|
14
14
|
overflow-x: auto;
|
|
15
|
+
scroll-padding-inline: var(--space-6);
|
|
15
16
|
margin-block-end: var(--space-4);
|
|
16
17
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/* A strip that scrolls fades each clipped edge; the scroll padding keeps a revealed tab clear
|
|
21
|
+
* of the fade. Mask stops are alpha only: #000 keeps a pixel and transparent hides it. */
|
|
22
|
+
.fg-tabs__list[data-clipped="right"] { mask-image: linear-gradient(to right, #000 calc(100% - var(--space-6)), transparent); }
|
|
23
|
+
.fg-tabs__list[data-clipped="left"] { mask-image: linear-gradient(to left, #000 calc(100% - var(--space-6)), transparent); }
|
|
24
|
+
.fg-tabs__list[data-clipped="both"] {
|
|
25
|
+
mask-image: linear-gradient(to right, transparent, #000 var(--space-6), #000 calc(100% - var(--space-6)), transparent);
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
.fg-tabs__tab {
|
|
20
29
|
flex: none;
|
|
21
30
|
min-height: var(--control-md);
|
package/src/site.css
CHANGED
|
@@ -27,7 +27,13 @@
|
|
|
27
27
|
.fg-site-docs :where(h1) { max-width: 22ch; font-size: var(--text-3xl); letter-spacing: var(--tracking-tight); }
|
|
28
28
|
.fg-site-docs :where(h2) { font-size: var(--text-xl); }
|
|
29
29
|
.fg-site-docs :where(p) { max-width: var(--measure); }
|
|
30
|
+
/* Tab panels and other enhanced markup use the hidden attribute, so it outranks the page's own
|
|
31
|
+
* display rules. until-found keeps the browser's find-in-page behavior. */
|
|
32
|
+
.fg-site-docs :where([hidden]:not([hidden="until-found"])) { display: none !important; }
|
|
30
33
|
.fg-site-docs :where(code, kbd, pre) { font-family: var(--font-mono); }
|
|
34
|
+
/* Monospace looks larger than the UI face, so inline code steps down: --text-sm in --text-md text.
|
|
35
|
+
* Code inside pre keeps the block's own size. */
|
|
36
|
+
.fg-site-docs :where(:not(pre) > code) { font-size: var(--text-sm); }
|
|
31
37
|
.fg-site-docs :where(a) { color: var(--color-link); text-underline-offset: 0.18em; }
|
|
32
38
|
.fg-site-docs :where(kbd) {
|
|
33
39
|
display: inline-block;
|
|
@@ -90,10 +96,11 @@
|
|
|
90
96
|
}
|
|
91
97
|
.fg-site-footer a:hover { color: var(--color-text); text-decoration: underline; }
|
|
92
98
|
|
|
93
|
-
/* Legend: a dl of what a capture shows, with one div per dt and dd pair.
|
|
99
|
+
/* Legend: a dl of what a capture shows, with one div per dt and dd pair. Columns are at
|
|
100
|
+
* least --site-legend-column wide (default --sidebar); a page can lower it to fit more. */
|
|
94
101
|
.fg-site-legend {
|
|
95
102
|
display: grid;
|
|
96
|
-
grid-template-columns: repeat(auto-fit, minmax(min(var(--sidebar), 100%), 1fr));
|
|
103
|
+
grid-template-columns: repeat(auto-fit, minmax(min(var(--site-legend-column, var(--sidebar)), 100%), 1fr));
|
|
97
104
|
gap: 0 var(--space-6);
|
|
98
105
|
margin: 0;
|
|
99
106
|
font-size: var(--text-sm);
|
|
@@ -513,6 +520,8 @@
|
|
|
513
520
|
}
|
|
514
521
|
.fg-site-definitions dt { font-weight: var(--weight-medium); }
|
|
515
522
|
.fg-site-definitions dd { margin: 0; color: var(--color-muted); }
|
|
523
|
+
/* Keep flags such as --all whole instead of breaking after a hyphen. */
|
|
524
|
+
.fg-site-definitions code { white-space: nowrap; }
|
|
516
525
|
|
|
517
526
|
.fg-site-sr-only {
|
|
518
527
|
position: absolute;
|
|
@@ -606,6 +615,16 @@
|
|
|
606
615
|
.fg-site-docs { padding-inline: var(--space-4); }
|
|
607
616
|
/* Docs-shell pages keep one header row on phones and repeat these links in their footer. */
|
|
608
617
|
.fg-site-docs .fg-site-header__links { display: none; }
|
|
618
|
+
/* Fact links, such as a Source link that stands in for the hidden header link, get a
|
|
619
|
+
* touch-sized hit area. The negative margin keeps each fact row one line tall, and the
|
|
620
|
+
* row gap of a list with links makes room for the part of the hit area past the line. */
|
|
621
|
+
.fg-site-facts:has(a) { row-gap: calc(var(--control-lg) - 1lh); }
|
|
622
|
+
.fg-site-facts a {
|
|
623
|
+
display: inline-flex;
|
|
624
|
+
min-height: var(--control-lg);
|
|
625
|
+
align-items: center;
|
|
626
|
+
margin-block: calc((1lh - var(--control-lg)) / 2);
|
|
627
|
+
}
|
|
609
628
|
.fg-dialog__content.fg-capture-viewer { width: 100%; max-width: none; }
|
|
610
629
|
/* Phones keep the icon; the accessible name still says Full size. */
|
|
611
630
|
.fg-capture[data-enhanced="true"] .fg-tabs__list { margin-inline-end: calc(var(--space-2) + var(--control-lg)); }
|
|
@@ -106,6 +106,6 @@
|
|
|
106
106
|
--color-warning-surface: color-mix(in srgb, var(--color-warning) 12%, var(--color-bg));
|
|
107
107
|
--color-success-surface: color-mix(in srgb, var(--color-success) 11%, var(--color-bg));
|
|
108
108
|
--color-info-surface: color-mix(in srgb, var(--color-info) 10%, var(--color-bg));
|
|
109
|
-
--shadow-dialog: 0 8px 24px rgb(0 0 0 / 45%), 0 0 0 1px var(--color-border);
|
|
109
|
+
--shadow-dialog: 0 8px 24px rgb(0 0 0 / 45%), 0 0 0 1px var(--color-border-subtle);
|
|
110
110
|
--color-status-text: var(--color-text);
|
|
111
111
|
}
|