@stacksjs/ui 0.70.88 → 0.70.90
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/dist/components/autocomplete.d.ts +1 -0
- package/dist/components/autocomplete.js +0 -0
- package/dist/components/disclosure.d.ts +4 -0
- package/dist/components/disclosure.js +1 -0
- package/dist/components/menu.d.ts +2 -0
- package/dist/components/menu.js +1 -0
- package/dist/components/modal.d.ts +6 -0
- package/dist/components/modal.js +6 -0
- package/dist/components/popover.d.ts +1 -0
- package/dist/components/popover.js +1 -0
- package/dist/components/radio-group.d.ts +5 -0
- package/dist/components/radio-group.js +5 -0
- package/dist/components/select.d.ts +6 -0
- package/dist/components/select.js +6 -0
- package/dist/components/tabs.d.ts +2 -0
- package/dist/components/tabs.js +1 -0
- package/dist/components/toggle.d.ts +1 -0
- package/dist/components/toggle.js +1 -0
- package/dist/components/transition.d.ts +2 -0
- package/dist/components/transition.js +1 -0
- package/dist/components.d.ts +10 -0
- package/dist/components.js +10 -0
- package/dist/fonts.d.ts +68 -0
- package/dist/fonts.js +53 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +14 -0
- package/dist/pagination.d.ts +71 -0
- package/dist/pagination.js +40 -0
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Popover, PopoverButton, PopoverPanel } from '@stacksjs/components';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Popover, PopoverButton, PopoverPanel } from "@stacksjs/components";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TabPanel, Tabs } from "@stacksjs/components";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Switch } from '@stacksjs/components';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Switch } from "@stacksjs/components";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Transition } from "@stacksjs/components";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './components/autocomplete';
|
|
2
|
+
export * from './components/disclosure';
|
|
3
|
+
export * from './components/menu';
|
|
4
|
+
export * from './components/modal';
|
|
5
|
+
export * from './components/popover';
|
|
6
|
+
export * from './components/radio-group';
|
|
7
|
+
export * from './components/select';
|
|
8
|
+
export * from './components/tabs';
|
|
9
|
+
export * from './components/toggle';
|
|
10
|
+
export * from './components/transition';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./components/autocomplete";
|
|
2
|
+
export * from "./components/disclosure";
|
|
3
|
+
export * from "./components/menu";
|
|
4
|
+
export * from "./components/modal";
|
|
5
|
+
export * from "./components/popover";
|
|
6
|
+
export * from "./components/radio-group";
|
|
7
|
+
export * from "./components/select";
|
|
8
|
+
export * from "./components/tabs";
|
|
9
|
+
export * from "./components/toggle";
|
|
10
|
+
export * from "./components/transition";
|
package/dist/fonts.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render `<link rel="preload" as="font" crossorigin>` tags for every
|
|
3
|
+
* font with `preload !== false`. Cross-origin attribute is always
|
|
4
|
+
* emitted because most CDN-hosted fonts require it and same-origin
|
|
5
|
+
* fonts ignore it harmlessly.
|
|
6
|
+
*
|
|
7
|
+
* Output is a single string with one tag per line, ready to drop into
|
|
8
|
+
* a stx layout's `<head>` via `{{ renderFontPreloads(fonts) }}`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderFontPreloads(fonts?: FontEntry[]): string;
|
|
11
|
+
/**
|
|
12
|
+
* Render an `@font-face { ... }` block for every entry. `font-display:
|
|
13
|
+
* swap` is the default; override per-entry with `display`.
|
|
14
|
+
*
|
|
15
|
+
* Wrap the result in `<style>` tags when inlining into a layout's
|
|
16
|
+
* `<head>`, or write to a separate CSS file and `<link>` it.
|
|
17
|
+
*/
|
|
18
|
+
export declare function renderFontFaceCss(fonts?: FontEntry[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* Convenience helper: render BOTH the preload tags and the wrapped
|
|
21
|
+
* `<style>...@font-face...</style>` block in one go, separated by a
|
|
22
|
+
* newline. Drop this into the very top of your layout's `<head>` for
|
|
23
|
+
* the maximal CLS win.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```stx
|
|
27
|
+
* <head>
|
|
28
|
+
* {{ renderFontHead(config.ui.fonts) }}
|
|
29
|
+
* <title>...</title>
|
|
30
|
+
* </head>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderFontHead(fonts?: FontEntry[]): string;
|
|
34
|
+
/**
|
|
35
|
+
* Web-font preload + `@font-face` rendering helpers
|
|
36
|
+
* (stacksjs/stacks#283 — prevent layout shifts).
|
|
37
|
+
*
|
|
38
|
+
* Layout shifts (the LS in CLS) happen when a page renders with the
|
|
39
|
+
* fallback font, then re-flows once the web font loads. The two
|
|
40
|
+
* mitigations this module ships against that:
|
|
41
|
+
*
|
|
42
|
+
* 1. **Preload** — emit `<link rel="preload" as="font" crossorigin>`
|
|
43
|
+
* for each font listed in the UI config, so the browser starts the
|
|
44
|
+
* font download in parallel with HTML parsing rather than waiting
|
|
45
|
+
* to discover the URL via CSS.
|
|
46
|
+
*
|
|
47
|
+
* 2. **`font-display: swap`** — the default `@font-face` block uses
|
|
48
|
+
* `font-display: swap`, which shows the fallback font immediately
|
|
49
|
+
* and swaps in the web font once available. Layout still shifts
|
|
50
|
+
* slightly at swap time, but the page is readable from t=0 instead
|
|
51
|
+
* of being blocked by the font fetch.
|
|
52
|
+
*
|
|
53
|
+
* Apps drop a list of fonts into `config/ui.ts` and call these helpers
|
|
54
|
+
* from their layout's `<head>` — see {@link renderFontHead}. Both
|
|
55
|
+
* outputs are safe to inline into HTML (no user-controlled values
|
|
56
|
+
* interpolate; the FontEntry shape is config-only).
|
|
57
|
+
*/
|
|
58
|
+
/** One configured web font. */
|
|
59
|
+
export declare interface FontEntry {
|
|
60
|
+
family: string
|
|
61
|
+
src: string
|
|
62
|
+
format?: 'woff2' | 'woff' | 'truetype' | 'opentype'
|
|
63
|
+
weight?: number | string
|
|
64
|
+
style?: 'normal' | 'italic' | 'oblique'
|
|
65
|
+
display?: 'auto' | 'swap' | 'block' | 'fallback' | 'optional'
|
|
66
|
+
preload?: boolean
|
|
67
|
+
unicodeRange?: string
|
|
68
|
+
}
|
package/dist/fonts.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const FORMAT_LABEL = {
|
|
2
|
+
woff2: "woff2",
|
|
3
|
+
woff: "woff",
|
|
4
|
+
truetype: "truetype",
|
|
5
|
+
opentype: "opentype"
|
|
6
|
+
}, FORMAT_MIME = {
|
|
7
|
+
woff2: "font/woff2",
|
|
8
|
+
woff: "font/woff",
|
|
9
|
+
truetype: "font/ttf",
|
|
10
|
+
opentype: "font/otf"
|
|
11
|
+
};
|
|
12
|
+
export function renderFontPreloads(fonts = []) {
|
|
13
|
+
return fonts.filter((font) => font.preload !== !1).map((font) => {
|
|
14
|
+
const fmt = font.format ?? "woff2", type = FORMAT_MIME[fmt];
|
|
15
|
+
return `<link rel="preload" href="${escapeAttr(font.src)}" as="font" type="${type}" crossorigin>`;
|
|
16
|
+
}).join(`
|
|
17
|
+
`);
|
|
18
|
+
}
|
|
19
|
+
export function renderFontFaceCss(fonts = []) {
|
|
20
|
+
return fonts.map((font) => {
|
|
21
|
+
const fmt = font.format ?? "woff2", fmtLabel = FORMAT_LABEL[fmt], weight = font.weight ?? "normal", style = font.style ?? "normal", display = font.display ?? "swap", ur = font.unicodeRange;
|
|
22
|
+
return `@font-face {
|
|
23
|
+
font-family: '${escapeFamily(font.family)}';
|
|
24
|
+
src: url('${escapeUrl(font.src)}') format('${fmtLabel}');
|
|
25
|
+
font-weight: ${weight};
|
|
26
|
+
font-style: ${style};
|
|
27
|
+
font-display: ${display};${ur ? `
|
|
28
|
+
unicode-range: ${ur};` : ""}
|
|
29
|
+
}`;
|
|
30
|
+
}).join(`
|
|
31
|
+
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
export function renderFontHead(fonts = []) {
|
|
35
|
+
if (fonts.length === 0)
|
|
36
|
+
return "";
|
|
37
|
+
const preloads = renderFontPreloads(fonts), faces = renderFontFaceCss(fonts);
|
|
38
|
+
return `${preloads}
|
|
39
|
+
<style>
|
|
40
|
+
${faces}
|
|
41
|
+
</style>`;
|
|
42
|
+
}
|
|
43
|
+
function escapeAttr(s) {
|
|
44
|
+
return String(s).replace(/[&<>"']/g, (c) => {
|
|
45
|
+
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c];
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function escapeUrl(s) {
|
|
49
|
+
return String(s).replace(/['"`()<>{};\\\r\n\t*]/g, "");
|
|
50
|
+
}
|
|
51
|
+
function escapeFamily(s) {
|
|
52
|
+
return String(s).replace(/[^A-Z0-9 \-_.]/gi, "");
|
|
53
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ui } from '@stacksjs/config';
|
|
2
|
+
import * as CssEngine from '@cwcss/crosswind';
|
|
3
|
+
export type { FontEntry } from './fonts';
|
|
4
|
+
export type { PaginatorView } from './pagination';
|
|
5
|
+
export { CssEngine, ui };
|
|
6
|
+
// Web-font preload + @font-face helpers — used by stx layouts to
|
|
7
|
+
// eliminate the FOUT/CLS that hits when a page renders with the
|
|
8
|
+
// fallback font and then re-flows once the web font lands
|
|
9
|
+
// (stacksjs/stacks#283).
|
|
10
|
+
export {
|
|
11
|
+
renderFontFaceCss,
|
|
12
|
+
renderFontHead,
|
|
13
|
+
renderFontPreloads,
|
|
14
|
+
} from './fonts';
|
|
15
|
+
// Pagination view helpers — pure functions consumed by the
|
|
16
|
+
// <Pagination> stx component (defaults/resources/components/Pagination.stx).
|
|
17
|
+
// stacksjs/stacks#1909 P5.
|
|
18
|
+
export {
|
|
19
|
+
buildPageSequence,
|
|
20
|
+
paginatorVariant,
|
|
21
|
+
urlForPage,
|
|
22
|
+
} from './pagination';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ui } from "@stacksjs/config";
|
|
2
|
+
import * as CssEngine from "@cwcss/crosswind";
|
|
3
|
+
|
|
4
|
+
export { CssEngine, ui };
|
|
5
|
+
export {
|
|
6
|
+
renderFontFaceCss,
|
|
7
|
+
renderFontHead,
|
|
8
|
+
renderFontPreloads
|
|
9
|
+
} from "./fonts";
|
|
10
|
+
export {
|
|
11
|
+
buildPageSequence,
|
|
12
|
+
paginatorVariant,
|
|
13
|
+
urlForPage
|
|
14
|
+
} from "./pagination";
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the page-number sequence for a full paginator, inserting an
|
|
3
|
+
* ellipsis placeholder (`'…'`) for the gap between page 1 / the
|
|
4
|
+
* current-page window / the last page.
|
|
5
|
+
*
|
|
6
|
+
* Examples (window=2):
|
|
7
|
+
*
|
|
8
|
+
* current=5, last=12 → [1, '…', 3, 4, 5, 6, 7, '…', 12]
|
|
9
|
+
* current=1, last=3 → [1, 2, 3] (window covers all)
|
|
10
|
+
* current=1, last=1 → [] (single page → no UI)
|
|
11
|
+
* current=5, last=5 → [1, 2, 3, 4, 5] (last is current, no trailing ellipsis)
|
|
12
|
+
*
|
|
13
|
+
* Always anchors the sequence with `1` and `last_page` (when they
|
|
14
|
+
* exist and differ from the current window) so users always have a
|
|
15
|
+
* "jump to start" / "jump to end" affordance.
|
|
16
|
+
*
|
|
17
|
+
* @param current 1-indexed current page
|
|
18
|
+
* @param last 1-indexed last page (`Paginator.last_page`)
|
|
19
|
+
* @param window Number of neighbors on EACH side of `current` to
|
|
20
|
+
* show before the ellipsis kicks in. Default 2 gives
|
|
21
|
+
* the canonical compact shape.
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildPageSequence(current: number, last: number, window?: number): Array<number | '…'>;
|
|
24
|
+
/**
|
|
25
|
+
* Compute the URL for a specific page number, re-templating the
|
|
26
|
+
* `page=N` parameter on whichever existing paginator URL is present.
|
|
27
|
+
* Preserves all other query params (search filters, sort, etc.) — the
|
|
28
|
+
* URLs filled in by `enrichPaginatorUrls()` (P2) already carry them,
|
|
29
|
+
* so the re-template just swaps the page number.
|
|
30
|
+
*
|
|
31
|
+
* Falls back to `?page=N` when no template URL is available — covers
|
|
32
|
+
* the case where the paginator was built outside a request scope (CLI
|
|
33
|
+
* / queue / cron) and rendered via a non-default view; produces a
|
|
34
|
+
* relative link that still works against the active page.
|
|
35
|
+
*/
|
|
36
|
+
export declare function urlForPage(view: PaginatorView, page: number): string;
|
|
37
|
+
/**
|
|
38
|
+
* Classify a paginator instance by shape so the view picks the right
|
|
39
|
+
* UI variant. Returns one of `'full'` / `'simple'` / `'cursor'` based
|
|
40
|
+
* on which fields are present. This mirrors `isPaginator` /
|
|
41
|
+
* `isSimplePaginator` / `isCursorPaginator` in `@stacksjs/orm` but
|
|
42
|
+
* lives here so the view layer doesn't need to import the orm.
|
|
43
|
+
*/
|
|
44
|
+
export declare function paginatorVariant(p: unknown): 'full' | 'simple' | 'cursor' | 'unknown';
|
|
45
|
+
/**
|
|
46
|
+
* Pagination view helpers (stacksjs/stacks#1909, P5 from #1910).
|
|
47
|
+
*
|
|
48
|
+
* Pure functions consumed by the `<Pagination>` stx component
|
|
49
|
+
* (`defaults/resources/components/Pagination.stx`). Extracted here so
|
|
50
|
+
* the page-sequence + URL-templating logic is unit-testable independent
|
|
51
|
+
* of the stx render pipeline, and so apps that want to roll their own
|
|
52
|
+
* pagination UI can reuse the same algorithms.
|
|
53
|
+
*
|
|
54
|
+
* The functions operate on the canonical paginator shapes from
|
|
55
|
+
* `@stacksjs/orm` (Paginator / SimplePaginator / CursorPaginator), but
|
|
56
|
+
* accept any duck-typed object with the right fields so callers don't
|
|
57
|
+
* need a runtime import dependency on the orm module just to format
|
|
58
|
+
* page numbers.
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
61
|
+
* Subset of {@link Paginator} fields that the view-side helpers actually
|
|
62
|
+
* touch. Keeping this minimal keeps the helper decoupled from the orm.
|
|
63
|
+
*/
|
|
64
|
+
export declare interface PaginatorView {
|
|
65
|
+
current_page?: number
|
|
66
|
+
last_page?: number
|
|
67
|
+
prev_page_url?: string | null
|
|
68
|
+
next_page_url?: string | null
|
|
69
|
+
first_page_url?: string
|
|
70
|
+
last_page_url?: string
|
|
71
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function buildPageSequence(current, last, window = 2) {
|
|
2
|
+
if (last <= 1)
|
|
3
|
+
return [];
|
|
4
|
+
const out = [], lo = Math.max(2, current - window), hi = Math.min(last - 1, current + window);
|
|
5
|
+
out.push(1);
|
|
6
|
+
if (lo === 3)
|
|
7
|
+
out.push(2);
|
|
8
|
+
else if (lo > 3)
|
|
9
|
+
out.push("\u2026");
|
|
10
|
+
for (let i = lo;i <= hi; i++)
|
|
11
|
+
out.push(i);
|
|
12
|
+
if (hi === last - 2)
|
|
13
|
+
out.push(last - 1);
|
|
14
|
+
else if (hi < last - 2)
|
|
15
|
+
out.push("\u2026");
|
|
16
|
+
if (last > 1)
|
|
17
|
+
out.push(last);
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
export function urlForPage(view, page) {
|
|
21
|
+
const template = view.next_page_url || view.prev_page_url || view.first_page_url || view.last_page_url;
|
|
22
|
+
if (template) {
|
|
23
|
+
if (/[?&]page=\d+/.test(template))
|
|
24
|
+
return template.replace(/([?&])page=\d+/, `$1page=${page}`);
|
|
25
|
+
return `${template}${template.includes("?") ? "&" : "?"}page=${page}`;
|
|
26
|
+
}
|
|
27
|
+
return `?page=${page}`;
|
|
28
|
+
}
|
|
29
|
+
export function paginatorVariant(p) {
|
|
30
|
+
if (p === null || typeof p !== "object")
|
|
31
|
+
return "unknown";
|
|
32
|
+
const v = p;
|
|
33
|
+
if ("next_cursor" in v)
|
|
34
|
+
return "cursor";
|
|
35
|
+
if ("total" in v && "last_page" in v)
|
|
36
|
+
return "full";
|
|
37
|
+
if ("current_page" in v && "per_page" in v)
|
|
38
|
+
return "simple";
|
|
39
|
+
return "unknown";
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.70.
|
|
4
|
+
"version": "0.70.90",
|
|
5
5
|
"description": "The Stacks UI engine.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"contributors": [
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@stacksjs/build": "workspace:*",
|
|
65
|
-
"@stacksjs/config": "0.70.
|
|
65
|
+
"@stacksjs/config": "0.70.90",
|
|
66
66
|
"better-dx": "^0.2.16",
|
|
67
|
-
"@stacksjs/router": "0.70.
|
|
67
|
+
"@stacksjs/router": "0.70.90"
|
|
68
68
|
}
|
|
69
69
|
}
|