@volksverpetzer/ui-web 0.12.1 → 0.13.1
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/PillGroup.css +52 -0
- package/dist/PillGroup.d.ts +25 -0
- package/dist/PillGroup.js +45 -0
- package/dist/ToolPage.css +8 -0
- package/dist/ToolPage.js +7 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/styles.css +61 -0
- package/package.json +3 -3
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
.vvp-ui-pill-group {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
flex-wrap: wrap;
|
|
4
|
+
gap: var(--vvp-spacing-sm, 8px);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.vvp-ui-pill-group__option {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
border: 2px solid var(--vvp-surface, #e2f0f5);
|
|
12
|
+
border-radius: var(--vvp-radius-full, 9999px);
|
|
13
|
+
background: var(--vvp-background, #fff);
|
|
14
|
+
color: var(--vvp-text, #111);
|
|
15
|
+
font-family: inherit;
|
|
16
|
+
font-weight: 700;
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
transition:
|
|
19
|
+
background-color 0.15s ease,
|
|
20
|
+
border-color 0.15s ease,
|
|
21
|
+
color 0.15s ease;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.vvp-ui-pill-group[data-size="md"] .vvp-ui-pill-group__option {
|
|
25
|
+
padding: var(--vvp-spacing-md, 10px) var(--vvp-spacing-xl, 20px);
|
|
26
|
+
font-size: var(--vvp-font-size-base, 16px);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.vvp-ui-pill-group[data-size="sm"] .vvp-ui-pill-group__option {
|
|
30
|
+
padding: var(--vvp-spacing-sm, 8px) var(--vvp-spacing-lg, 16px);
|
|
31
|
+
font-size: var(--vvp-font-size-sm, 14px);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.vvp-ui-pill-group__option:hover:not(:disabled) {
|
|
35
|
+
border-color: var(--vvp-primary-muted, #3893c0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.vvp-ui-pill-group__option:focus-visible {
|
|
39
|
+
outline: 2px solid var(--vvp-primary-muted, #3893c0);
|
|
40
|
+
outline-offset: 2px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.vvp-ui-pill-group__option:disabled {
|
|
44
|
+
opacity: 0.6;
|
|
45
|
+
cursor: not-allowed;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.vvp-ui-pill-group__option[aria-checked="true"] {
|
|
49
|
+
background: var(--vvp-primary, #1b7194);
|
|
50
|
+
border-color: var(--vvp-primary, #1b7194);
|
|
51
|
+
color: var(--vvp-on-primary, #fff);
|
|
52
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "./PillGroup.css";
|
|
2
|
+
export interface PillGroupOption {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface PillGroupProps {
|
|
8
|
+
/** Visually hidden unless `label` is also rendered by the consumer via `aria-labelledby` — falls back to this as the accessible name. */
|
|
9
|
+
"aria-label"?: string;
|
|
10
|
+
options: PillGroupOption[];
|
|
11
|
+
value: string;
|
|
12
|
+
onChange: (value: string) => void;
|
|
13
|
+
size?: "sm" | "md";
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A row of pill-shaped options for choosing one of a small, fixed set of
|
|
19
|
+
* values — the house alternative to a native `<select>` (see vvp_app's
|
|
20
|
+
* "pills over dropdowns" convention, now ported to web). Implements the
|
|
21
|
+
* ARIA APG radiogroup pattern: one `role="radio"` per pill, roving
|
|
22
|
+
* tabindex, and Left/Right (and Up/Down) arrow keys move both focus and
|
|
23
|
+
* selection, matching native `<select>`/radio-group keyboard behavior.
|
|
24
|
+
*/
|
|
25
|
+
export declare function PillGroup({ "aria-label": ariaLabel, options, value, onChange, size, disabled, className, }: PillGroupProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import "./PillGroup.css";
|
|
4
|
+
/**
|
|
5
|
+
* A row of pill-shaped options for choosing one of a small, fixed set of
|
|
6
|
+
* values — the house alternative to a native `<select>` (see vvp_app's
|
|
7
|
+
* "pills over dropdowns" convention, now ported to web). Implements the
|
|
8
|
+
* ARIA APG radiogroup pattern: one `role="radio"` per pill, roving
|
|
9
|
+
* tabindex, and Left/Right (and Up/Down) arrow keys move both focus and
|
|
10
|
+
* selection, matching native `<select>`/radio-group keyboard behavior.
|
|
11
|
+
*/
|
|
12
|
+
export function PillGroup({ "aria-label": ariaLabel, options, value, onChange, size = "md", disabled = false, className, }) {
|
|
13
|
+
const groupId = useId();
|
|
14
|
+
const selectNext = (fromIndex, direction) => {
|
|
15
|
+
const enabled = options
|
|
16
|
+
.map((opt, i) => ({ opt, i }))
|
|
17
|
+
.filter(({ opt }) => !opt.disabled);
|
|
18
|
+
if (enabled.length === 0)
|
|
19
|
+
return;
|
|
20
|
+
const currentPos = enabled.findIndex(({ i }) => i === fromIndex);
|
|
21
|
+
const nextPos = (currentPos + direction + enabled.length) % enabled.length;
|
|
22
|
+
const next = enabled[nextPos];
|
|
23
|
+
if (next)
|
|
24
|
+
onChange(next.opt.value);
|
|
25
|
+
};
|
|
26
|
+
const handleKeyDown = (e, i) => {
|
|
27
|
+
switch (e.key) {
|
|
28
|
+
case "ArrowRight":
|
|
29
|
+
case "ArrowDown":
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
selectNext(i, 1);
|
|
32
|
+
break;
|
|
33
|
+
case "ArrowLeft":
|
|
34
|
+
case "ArrowUp":
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
selectNext(i, -1);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const classes = ["vvp-ui-pill-group", className].filter(Boolean).join(" ");
|
|
41
|
+
return (_jsx("div", { role: "radiogroup", "aria-label": ariaLabel, className: classes, "data-size": size, children: options.map((option, i) => {
|
|
42
|
+
const checked = option.value === value;
|
|
43
|
+
return (_jsx("button", { id: `${groupId}-${option.value}`, type: "button", role: "radio", "aria-checked": checked, disabled: disabled || option.disabled, tabIndex: checked ? 0 : -1, className: "vvp-ui-pill-group__option", onClick: () => onChange(option.value), onKeyDown: (e) => handleKeyDown(e, i), children: option.label }, option.value));
|
|
44
|
+
}) }));
|
|
45
|
+
}
|
package/dist/ToolPage.css
CHANGED
|
@@ -132,6 +132,14 @@
|
|
|
132
132
|
gap: var(--vvp-spacing-xs, 4px);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/*
|
|
136
|
+
* The decorative background is 220px tall and fades out; without extra space
|
|
137
|
+
* the content slot (a card, form, list) starts on top of the jagged edge.
|
|
138
|
+
*/
|
|
139
|
+
.vvp-ui-tool-page--with-background .vvp-ui-tool-page__content {
|
|
140
|
+
margin-top: var(--vvp-spacing-huge, 40px);
|
|
141
|
+
}
|
|
142
|
+
|
|
135
143
|
.vvp-ui-tool-page__content {
|
|
136
144
|
width: 100%;
|
|
137
145
|
}
|
package/dist/ToolPage.js
CHANGED
|
@@ -14,7 +14,13 @@ const GithubIcon = () => (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", wid
|
|
|
14
14
|
* photo) instead of the abstract shape.
|
|
15
15
|
*/
|
|
16
16
|
export function ToolPage({ icon, title, tags, children, helpText, githubUrl, background = true, headerImage, centered = false, className, ...rest }) {
|
|
17
|
-
const classes = [
|
|
17
|
+
const classes = [
|
|
18
|
+
"vvp-ui-tool-page",
|
|
19
|
+
!headerImage && background && "vvp-ui-tool-page--with-background",
|
|
20
|
+
className,
|
|
21
|
+
]
|
|
22
|
+
.filter(Boolean)
|
|
23
|
+
.join(" ");
|
|
18
24
|
const innerClasses = [
|
|
19
25
|
"vvp-ui-tool-page__inner",
|
|
20
26
|
centered && "vvp-ui-tool-page__inner--centered",
|
package/dist/index.d.ts
CHANGED
|
@@ -8,4 +8,5 @@ export { Alert, type AlertProps, type AlertVariant } from "./Alert";
|
|
|
8
8
|
export { Slider, type SliderProps } from "./Slider";
|
|
9
9
|
export { ThemeToggle } from "./ThemeToggle";
|
|
10
10
|
export { InputButton, type InputButtonProps } from "./InputButton";
|
|
11
|
+
export { PillGroup, type PillGroupProps, type PillGroupOption, } from "./PillGroup";
|
|
11
12
|
export { ToolPage, type ToolPageProps, type ToolPageTag } from "./ToolPage";
|
package/dist/index.js
CHANGED
package/dist/styles.css
CHANGED
|
@@ -309,6 +309,59 @@
|
|
|
309
309
|
);
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
.vvp-ui-pill-group {
|
|
313
|
+
display: inline-flex;
|
|
314
|
+
flex-wrap: wrap;
|
|
315
|
+
gap: var(--vvp-spacing-sm, 8px);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.vvp-ui-pill-group__option {
|
|
319
|
+
display: inline-flex;
|
|
320
|
+
align-items: center;
|
|
321
|
+
justify-content: center;
|
|
322
|
+
border: 2px solid var(--vvp-surface, #e2f0f5);
|
|
323
|
+
border-radius: var(--vvp-radius-full, 9999px);
|
|
324
|
+
background: var(--vvp-background, #fff);
|
|
325
|
+
color: var(--vvp-text, #111);
|
|
326
|
+
font-family: inherit;
|
|
327
|
+
font-weight: 700;
|
|
328
|
+
cursor: pointer;
|
|
329
|
+
transition:
|
|
330
|
+
background-color 0.15s ease,
|
|
331
|
+
border-color 0.15s ease,
|
|
332
|
+
color 0.15s ease;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.vvp-ui-pill-group[data-size="md"] .vvp-ui-pill-group__option {
|
|
336
|
+
padding: var(--vvp-spacing-md, 10px) var(--vvp-spacing-xl, 20px);
|
|
337
|
+
font-size: var(--vvp-font-size-base, 16px);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.vvp-ui-pill-group[data-size="sm"] .vvp-ui-pill-group__option {
|
|
341
|
+
padding: var(--vvp-spacing-sm, 8px) var(--vvp-spacing-lg, 16px);
|
|
342
|
+
font-size: var(--vvp-font-size-sm, 14px);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.vvp-ui-pill-group__option:hover:not(:disabled) {
|
|
346
|
+
border-color: var(--vvp-primary-muted, #3893c0);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.vvp-ui-pill-group__option:focus-visible {
|
|
350
|
+
outline: 2px solid var(--vvp-primary-muted, #3893c0);
|
|
351
|
+
outline-offset: 2px;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.vvp-ui-pill-group__option:disabled {
|
|
355
|
+
opacity: 0.6;
|
|
356
|
+
cursor: not-allowed;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.vvp-ui-pill-group__option[aria-checked="true"] {
|
|
360
|
+
background: var(--vvp-primary, #1b7194);
|
|
361
|
+
border-color: var(--vvp-primary, #1b7194);
|
|
362
|
+
color: var(--vvp-on-primary, #fff);
|
|
363
|
+
}
|
|
364
|
+
|
|
312
365
|
.vvp-ui-progress {
|
|
313
366
|
width: 100%;
|
|
314
367
|
text-align: center;
|
|
@@ -603,6 +656,14 @@
|
|
|
603
656
|
gap: var(--vvp-spacing-xs, 4px);
|
|
604
657
|
}
|
|
605
658
|
|
|
659
|
+
/*
|
|
660
|
+
* The decorative background is 220px tall and fades out; without extra space
|
|
661
|
+
* the content slot (a card, form, list) starts on top of the jagged edge.
|
|
662
|
+
*/
|
|
663
|
+
.vvp-ui-tool-page--with-background .vvp-ui-tool-page__content {
|
|
664
|
+
margin-top: var(--vvp-spacing-huge, 40px);
|
|
665
|
+
}
|
|
666
|
+
|
|
606
667
|
.vvp-ui-tool-page__content {
|
|
607
668
|
width: 100%;
|
|
608
669
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volksverpetzer/ui-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Shared brand-visible React components (Button, Badge, Card, ProgressBar, Input, Alert, Slider, ThemeToggle, InputButton) for vvp_link_shortener, vvp_divi5_extensions, crowdfunding, and vectorcrawl. Plain, hand-scoped CSS (vvp-ui-* class prefix) consuming the --vvp-* custom properties emitted by @volksverpetzer/design-tokens — no Tailwind classes, so it drops into a Tailwind app and Divi's WordPress-embedded CSS alike.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"react": ">=19"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@types/react": "^19.
|
|
31
|
-
"react": "^19.
|
|
30
|
+
"@types/react": "^19.3.0",
|
|
31
|
+
"react": "^19.3.0",
|
|
32
32
|
"typescript": "^6.0.3"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|