rizzo-css 0.0.61 → 0.0.63
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 +9 -5
- package/bin/rizzo-css.js +568 -79
- package/dist/rizzo.min.css +5 -3
- package/package.json +14 -7
- package/scaffold/astro/Footer.astro +8 -0
- package/scaffold/astro/Settings.astro +8 -2
- package/scaffold/astro/Tabs.astro +2 -2
- package/scaffold/react/Accordion.tsx +143 -0
- package/scaffold/react/Alert.tsx +90 -0
- package/scaffold/react/AlertDialog.tsx +80 -0
- package/scaffold/react/AspectRatio.tsx +32 -0
- package/scaffold/react/Avatar.tsx +53 -0
- package/scaffold/react/BackToTop.tsx +62 -0
- package/scaffold/react/Badge.tsx +39 -0
- package/scaffold/react/Breadcrumb.tsx +81 -0
- package/scaffold/react/Button.tsx +40 -0
- package/scaffold/react/ButtonGroup.tsx +24 -0
- package/scaffold/react/Card.tsx +26 -0
- package/scaffold/react/Checkbox.tsx +40 -0
- package/scaffold/react/Collapsible.tsx +58 -0
- package/scaffold/react/ContextMenu.tsx +67 -0
- package/scaffold/react/CopyToClipboard.tsx +128 -0
- package/scaffold/react/Dashboard.tsx +23 -0
- package/scaffold/react/Divider.tsx +47 -0
- package/scaffold/react/DocsSidebar.tsx +48 -0
- package/scaffold/react/Dropdown.tsx +256 -0
- package/scaffold/react/Empty.tsx +29 -0
- package/scaffold/react/FontSwitcher.tsx +68 -0
- package/scaffold/react/Footer.tsx +55 -0
- package/scaffold/react/FormGroup.tsx +57 -0
- package/scaffold/react/HoverCard.tsx +61 -0
- package/scaffold/react/Icons.tsx +22 -0
- package/scaffold/react/Input.tsx +69 -0
- package/scaffold/react/Kbd.tsx +16 -0
- package/scaffold/react/Label.tsx +16 -0
- package/scaffold/react/Modal.tsx +149 -0
- package/scaffold/react/Navbar.tsx +72 -0
- package/scaffold/react/Pagination.tsx +155 -0
- package/scaffold/react/Popover.tsx +66 -0
- package/scaffold/react/ProgressBar.tsx +66 -0
- package/scaffold/react/Radio.tsx +38 -0
- package/scaffold/react/ResizableHandle.tsx +24 -0
- package/scaffold/react/ResizablePane.tsx +29 -0
- package/scaffold/react/ResizablePaneGroup.tsx +29 -0
- package/scaffold/react/ScrollArea.tsx +29 -0
- package/scaffold/react/Search.tsx +62 -0
- package/scaffold/react/Select.tsx +65 -0
- package/scaffold/react/Separator.tsx +33 -0
- package/scaffold/react/Settings.tsx +60 -0
- package/scaffold/react/Sheet.tsx +86 -0
- package/scaffold/react/Skeleton.tsx +32 -0
- package/scaffold/react/Slider.tsx +66 -0
- package/scaffold/react/SoundEffects.tsx +15 -0
- package/scaffold/react/Spinner.tsx +36 -0
- package/scaffold/react/Switch.tsx +52 -0
- package/scaffold/react/Table.tsx +178 -0
- package/scaffold/react/Tabs.tsx +143 -0
- package/scaffold/react/Textarea.tsx +69 -0
- package/scaffold/react/ThemeSwitcher.tsx +89 -0
- package/scaffold/react/Toast.tsx +43 -0
- package/scaffold/react/Toggle.tsx +45 -0
- package/scaffold/react/ToggleGroup.tsx +34 -0
- package/scaffold/react/Tooltip.tsx +40 -0
- package/scaffold/vanilla/README-RIZZO.md +1 -1
- package/scaffold/vanilla/components/accordion.html +40 -0
- package/scaffold/vanilla/components/alert-dialog.html +40 -0
- package/scaffold/vanilla/components/alert.html +40 -0
- package/scaffold/vanilla/components/aspect-ratio.html +40 -0
- package/scaffold/vanilla/components/avatar.html +40 -0
- package/scaffold/vanilla/components/back-to-top.html +40 -0
- package/scaffold/vanilla/components/badge.html +40 -0
- package/scaffold/vanilla/components/breadcrumb.html +40 -0
- package/scaffold/vanilla/components/button-group.html +40 -0
- package/scaffold/vanilla/components/button.html +40 -0
- package/scaffold/vanilla/components/cards.html +40 -0
- package/scaffold/vanilla/components/collapsible.html +40 -0
- package/scaffold/vanilla/components/context-menu.html +40 -0
- package/scaffold/vanilla/components/copy-to-clipboard.html +40 -0
- package/scaffold/vanilla/components/dashboard.html +40 -0
- package/scaffold/vanilla/components/divider.html +40 -0
- package/scaffold/vanilla/components/docs-sidebar.html +40 -0
- package/scaffold/vanilla/components/dropdown.html +40 -0
- package/scaffold/vanilla/components/empty.html +40 -0
- package/scaffold/vanilla/components/font-switcher.html +40 -0
- package/scaffold/vanilla/components/footer.html +40 -0
- package/scaffold/vanilla/components/forms.html +40 -0
- package/scaffold/vanilla/components/hover-card.html +40 -0
- package/scaffold/vanilla/components/icons.html +40 -0
- package/scaffold/vanilla/components/index.html +40 -0
- package/scaffold/vanilla/components/kbd.html +40 -0
- package/scaffold/vanilla/components/label.html +40 -0
- package/scaffold/vanilla/components/modal.html +40 -0
- package/scaffold/vanilla/components/navbar.html +40 -0
- package/scaffold/vanilla/components/pagination.html +40 -0
- package/scaffold/vanilla/components/popover.html +40 -0
- package/scaffold/vanilla/components/progress-bar.html +40 -0
- package/scaffold/vanilla/components/resizable.html +40 -0
- package/scaffold/vanilla/components/scroll-area.html +40 -0
- package/scaffold/vanilla/components/search.html +40 -0
- package/scaffold/vanilla/components/separator.html +40 -0
- package/scaffold/vanilla/components/settings.html +40 -0
- package/scaffold/vanilla/components/sheet.html +40 -0
- package/scaffold/vanilla/components/skeleton.html +40 -0
- package/scaffold/vanilla/components/slider.html +40 -0
- package/scaffold/vanilla/components/sound-effects.html +40 -0
- package/scaffold/vanilla/components/spinner.html +40 -0
- package/scaffold/vanilla/components/switch.html +40 -0
- package/scaffold/vanilla/components/table.html +40 -0
- package/scaffold/vanilla/components/tabs.html +40 -0
- package/scaffold/vanilla/components/theme-switcher.html +40 -0
- package/scaffold/vanilla/components/toast.html +40 -0
- package/scaffold/vanilla/components/toggle-group.html +40 -0
- package/scaffold/vanilla/components/toggle.html +40 -0
- package/scaffold/vanilla/components/tooltip.html +40 -0
- package/scaffold/vanilla/index.html +40 -0
- package/scaffold/vue/Accordion.vue +9 -0
- package/scaffold/vue/Alert.vue +9 -0
- package/scaffold/vue/AlertDialog.vue +9 -0
- package/scaffold/vue/AspectRatio.vue +9 -0
- package/scaffold/vue/Avatar.vue +9 -0
- package/scaffold/vue/BackToTop.vue +9 -0
- package/scaffold/vue/Badge.vue +28 -0
- package/scaffold/vue/Breadcrumb.vue +9 -0
- package/scaffold/vue/Button.vue +23 -0
- package/scaffold/vue/ButtonGroup.vue +9 -0
- package/scaffold/vue/Card.vue +21 -0
- package/scaffold/vue/Checkbox.vue +31 -0
- package/scaffold/vue/Collapsible.vue +9 -0
- package/scaffold/vue/ContextMenu.vue +9 -0
- package/scaffold/vue/CopyToClipboard.vue +9 -0
- package/scaffold/vue/Dashboard.vue +9 -0
- package/scaffold/vue/Divider.vue +23 -0
- package/scaffold/vue/DocsSidebar.vue +9 -0
- package/scaffold/vue/Dropdown.vue +9 -0
- package/scaffold/vue/Empty.vue +9 -0
- package/scaffold/vue/FontSwitcher.vue +9 -0
- package/scaffold/vue/Footer.vue +9 -0
- package/scaffold/vue/FormGroup.vue +45 -0
- package/scaffold/vue/HoverCard.vue +9 -0
- package/scaffold/vue/Icons.vue +9 -0
- package/scaffold/vue/Input.vue +59 -0
- package/scaffold/vue/Kbd.vue +9 -0
- package/scaffold/vue/Label.vue +23 -0
- package/scaffold/vue/Modal.vue +9 -0
- package/scaffold/vue/Navbar.vue +9 -0
- package/scaffold/vue/Pagination.vue +9 -0
- package/scaffold/vue/Popover.vue +9 -0
- package/scaffold/vue/ProgressBar.vue +9 -0
- package/scaffold/vue/Radio.vue +29 -0
- package/scaffold/vue/ResizableHandle.vue +9 -0
- package/scaffold/vue/ResizablePane.vue +9 -0
- package/scaffold/vue/ResizablePaneGroup.vue +9 -0
- package/scaffold/vue/ScrollArea.vue +9 -0
- package/scaffold/vue/Search.vue +9 -0
- package/scaffold/vue/Select.vue +52 -0
- package/scaffold/vue/Separator.vue +9 -0
- package/scaffold/vue/Settings.vue +9 -0
- package/scaffold/vue/Sheet.vue +9 -0
- package/scaffold/vue/Skeleton.vue +9 -0
- package/scaffold/vue/Slider.vue +9 -0
- package/scaffold/vue/SoundEffects.vue +9 -0
- package/scaffold/vue/Spinner.vue +21 -0
- package/scaffold/vue/Switch.vue +9 -0
- package/scaffold/vue/Table.vue +9 -0
- package/scaffold/vue/Tabs.vue +9 -0
- package/scaffold/vue/Textarea.vue +60 -0
- package/scaffold/vue/ThemeSwitcher.vue +9 -0
- package/scaffold/vue/Toast.vue +9 -0
- package/scaffold/vue/Toggle.vue +9 -0
- package/scaffold/vue/ToggleGroup.vue +9 -0
- package/scaffold/vue/Tooltip.vue +9 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { useState, useCallback, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
export interface Tab {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
icon?: string;
|
|
8
|
+
content?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TabsProps extends HTMLAttributes<HTMLDivElement> {
|
|
12
|
+
tabs: Tab[];
|
|
13
|
+
id?: string;
|
|
14
|
+
defaultTab?: string;
|
|
15
|
+
variant?: 'default' | 'pills' | 'underline';
|
|
16
|
+
children?: (activeTabId: string) => ReactNode;
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function Tabs({
|
|
21
|
+
tabs,
|
|
22
|
+
id: idProp,
|
|
23
|
+
defaultTab,
|
|
24
|
+
variant = 'default',
|
|
25
|
+
children,
|
|
26
|
+
className = '',
|
|
27
|
+
...rest
|
|
28
|
+
}: TabsProps) {
|
|
29
|
+
const defaultActiveId = defaultTab ?? tabs[0]?.id ?? '';
|
|
30
|
+
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
31
|
+
const activeTabId = selectedId ?? defaultActiveId;
|
|
32
|
+
const tabsId = idProp ?? `tabs-${Math.random().toString(36).slice(2, 11)}`;
|
|
33
|
+
const variantClass = variant !== 'default' ? `tabs--${variant}` : '';
|
|
34
|
+
const classes = ['tabs', variantClass, className].filter(Boolean).join(' ').trim();
|
|
35
|
+
const tabListRef = useRef<HTMLDivElement>(null);
|
|
36
|
+
|
|
37
|
+
const activateTab = useCallback(
|
|
38
|
+
(index: number) => {
|
|
39
|
+
if (index < 0 || index >= tabs.length) return;
|
|
40
|
+
setSelectedId(tabs[index].id);
|
|
41
|
+
},
|
|
42
|
+
[tabs]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const handleKeydown = useCallback(
|
|
46
|
+
(e: React.KeyboardEvent, index: number) => {
|
|
47
|
+
let targetIndex = index;
|
|
48
|
+
switch (e.key) {
|
|
49
|
+
case 'ArrowRight':
|
|
50
|
+
case 'ArrowDown':
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
targetIndex = (index + 1) % tabs.length;
|
|
53
|
+
break;
|
|
54
|
+
case 'ArrowLeft':
|
|
55
|
+
case 'ArrowUp':
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
targetIndex = index === 0 ? tabs.length - 1 : index - 1;
|
|
58
|
+
break;
|
|
59
|
+
case 'Home':
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
targetIndex = 0;
|
|
62
|
+
break;
|
|
63
|
+
case 'End':
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
targetIndex = tabs.length - 1;
|
|
66
|
+
break;
|
|
67
|
+
case 'Enter':
|
|
68
|
+
case ' ':
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
activateTab(index);
|
|
71
|
+
return;
|
|
72
|
+
default:
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
activateTab(targetIndex);
|
|
76
|
+
const buttons = tabListRef.current?.querySelectorAll('[role="tab"]');
|
|
77
|
+
if (buttons && buttons[targetIndex]) (buttons[targetIndex] as HTMLElement).focus();
|
|
78
|
+
},
|
|
79
|
+
[tabs.length, activateTab]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className={classes} data-tabs={tabsId} {...rest}>
|
|
84
|
+
<div className="tabs__list" role="tablist" aria-label="Tabs" ref={tabListRef}>
|
|
85
|
+
{tabs.map((tab, index) => {
|
|
86
|
+
const isActive = tab.id === activeTabId;
|
|
87
|
+
return (
|
|
88
|
+
<span
|
|
89
|
+
key={tab.id}
|
|
90
|
+
className={`tabs__tab ${isActive ? 'tabs__tab--active' : ''}`.trim()}
|
|
91
|
+
id={`${tabsId}-tab-${tab.id}`}
|
|
92
|
+
role="tab"
|
|
93
|
+
tabIndex={isActive ? 0 : -1}
|
|
94
|
+
aria-selected={isActive ? 'true' : 'false'}
|
|
95
|
+
aria-controls={`${tabsId}-panel-${tab.id}`}
|
|
96
|
+
data-tab-id={tab.id}
|
|
97
|
+
data-tab-index={index}
|
|
98
|
+
onClick={() => activateTab(index)}
|
|
99
|
+
onKeyDown={(e) => handleKeydown(e, index)}
|
|
100
|
+
>
|
|
101
|
+
{tab.icon && (
|
|
102
|
+
<img
|
|
103
|
+
src={tab.icon}
|
|
104
|
+
alt=""
|
|
105
|
+
className="tabs__tab-icon"
|
|
106
|
+
width={20}
|
|
107
|
+
height={20}
|
|
108
|
+
loading="lazy"
|
|
109
|
+
aria-hidden="true"
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
{tab.label}
|
|
113
|
+
</span>
|
|
114
|
+
);
|
|
115
|
+
})}
|
|
116
|
+
</div>
|
|
117
|
+
<div className="tabs__panels-wrapper">
|
|
118
|
+
{tabs.map((tab) => {
|
|
119
|
+
const isActive = tab.id === activeTabId;
|
|
120
|
+
return (
|
|
121
|
+
<div
|
|
122
|
+
key={tab.id}
|
|
123
|
+
className={`tabs__panel ${isActive ? 'tabs__panel--active' : ''}`.trim()}
|
|
124
|
+
id={`${tabsId}-panel-${tab.id}`}
|
|
125
|
+
role="tabpanel"
|
|
126
|
+
aria-labelledby={`${tabsId}-tab-${tab.id}`}
|
|
127
|
+
aria-hidden={isActive ? 'false' : 'true'}
|
|
128
|
+
data-panel-id={tab.id}
|
|
129
|
+
>
|
|
130
|
+
{tab.content ? (
|
|
131
|
+
<div className="tabs__panel-content" dangerouslySetInnerHTML={{ __html: tab.content }} />
|
|
132
|
+
) : isActive && children ? (
|
|
133
|
+
children(activeTabId)
|
|
134
|
+
) : null}
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
})}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export default Tabs;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { TextareaHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
export type TextareaSize = 'sm' | 'md' | 'lg';
|
|
4
|
+
|
|
5
|
+
export interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> {
|
|
6
|
+
size?: TextareaSize;
|
|
7
|
+
error?: boolean;
|
|
8
|
+
success?: boolean;
|
|
9
|
+
ariaDescribedby?: string;
|
|
10
|
+
ariaInvalid?: boolean | 'true' | 'false';
|
|
11
|
+
onValueChange?: (value: string) => void;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function Textarea({
|
|
16
|
+
id,
|
|
17
|
+
name,
|
|
18
|
+
value = '',
|
|
19
|
+
placeholder,
|
|
20
|
+
required = false,
|
|
21
|
+
disabled = false,
|
|
22
|
+
readOnly = false,
|
|
23
|
+
rows = 4,
|
|
24
|
+
cols,
|
|
25
|
+
size = 'md',
|
|
26
|
+
error = false,
|
|
27
|
+
success = false,
|
|
28
|
+
className = '',
|
|
29
|
+
ariaDescribedby,
|
|
30
|
+
ariaInvalid,
|
|
31
|
+
onChange,
|
|
32
|
+
onValueChange,
|
|
33
|
+
...rest
|
|
34
|
+
}: TextareaProps) {
|
|
35
|
+
const sizeClass = size !== 'md' ? `form-input--${size}` : '';
|
|
36
|
+
const errorClass = error ? 'form-input--error' : '';
|
|
37
|
+
const successClass = success ? 'form-input--success' : '';
|
|
38
|
+
const classes = ['form-input', sizeClass, errorClass, successClass, className]
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.join(' ')
|
|
41
|
+
.trim();
|
|
42
|
+
const invalid = error || ariaInvalid === true || ariaInvalid === 'true';
|
|
43
|
+
|
|
44
|
+
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
45
|
+
onChange?.(e);
|
|
46
|
+
onValueChange?.(e.target.value);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<textarea
|
|
51
|
+
id={id}
|
|
52
|
+
name={name}
|
|
53
|
+
value={value}
|
|
54
|
+
placeholder={placeholder}
|
|
55
|
+
required={required}
|
|
56
|
+
disabled={disabled}
|
|
57
|
+
readOnly={readOnly}
|
|
58
|
+
rows={rows}
|
|
59
|
+
cols={cols}
|
|
60
|
+
className={classes}
|
|
61
|
+
aria-invalid={invalid ? 'true' : 'false'}
|
|
62
|
+
aria-describedby={ariaDescribedby}
|
|
63
|
+
onChange={handleChange}
|
|
64
|
+
{...rest}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default Textarea;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
export interface ThemeOption {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ThemeSwitcherProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
idPrefix?: string;
|
|
11
|
+
themes?: ThemeOption[];
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_THEMES: ThemeOption[] = [
|
|
16
|
+
{ value: 'github-dark-classic', label: 'GitHub Dark' },
|
|
17
|
+
{ value: 'github-light', label: 'GitHub Light' },
|
|
18
|
+
{ value: 'system', label: 'System' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function ThemeSwitcher({
|
|
22
|
+
idPrefix = '',
|
|
23
|
+
themes = DEFAULT_THEMES,
|
|
24
|
+
className = '',
|
|
25
|
+
...rest
|
|
26
|
+
}: ThemeSwitcherProps) {
|
|
27
|
+
const [open, setOpen] = useState(false);
|
|
28
|
+
const [current, setCurrent] = useState(themes[0]?.value ?? 'system');
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const stored = typeof localStorage !== 'undefined' ? localStorage.getItem('theme') : null;
|
|
32
|
+
if (stored) setCurrent(stored);
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const menuId = idPrefix ? `theme-switcher-${idPrefix}-menu` : 'theme-switcher-menu';
|
|
36
|
+
const triggerId = idPrefix ? `theme-switcher-${idPrefix}-trigger` : 'theme-switcher-trigger';
|
|
37
|
+
|
|
38
|
+
const apply = (value: string) => {
|
|
39
|
+
if (value === 'system') {
|
|
40
|
+
document.documentElement.removeAttribute('data-theme');
|
|
41
|
+
localStorage.removeItem('theme');
|
|
42
|
+
} else {
|
|
43
|
+
document.documentElement.setAttribute('data-theme', value);
|
|
44
|
+
localStorage.setItem('theme', value);
|
|
45
|
+
}
|
|
46
|
+
setCurrent(value);
|
|
47
|
+
setOpen(false);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className={`theme-switcher ${className}`.trim()} data-theme-switcher {...rest}>
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
id={triggerId}
|
|
55
|
+
className="theme-switcher__trigger"
|
|
56
|
+
aria-expanded={open}
|
|
57
|
+
aria-haspopup="true"
|
|
58
|
+
aria-controls={menuId}
|
|
59
|
+
aria-label="Theme"
|
|
60
|
+
onClick={() => setOpen((o) => !o)}
|
|
61
|
+
>
|
|
62
|
+
<span className="theme-switcher__trigger-icon" aria-hidden="true">◐</span>
|
|
63
|
+
<span className="theme-switcher__trigger-label">{themes.find((t) => t.value === current)?.label ?? 'Theme'}</span>
|
|
64
|
+
</button>
|
|
65
|
+
<div
|
|
66
|
+
className={`theme-switcher__menu ${open ? 'theme-switcher__menu--open' : ''}`.trim()}
|
|
67
|
+
id={menuId}
|
|
68
|
+
role="menu"
|
|
69
|
+
aria-labelledby={triggerId}
|
|
70
|
+
aria-hidden={!open}
|
|
71
|
+
hidden={!open}
|
|
72
|
+
>
|
|
73
|
+
{themes.map((t) => (
|
|
74
|
+
<button
|
|
75
|
+
key={t.value}
|
|
76
|
+
type="button"
|
|
77
|
+
role="menuitem"
|
|
78
|
+
className={`theme-switcher__item ${current === t.value ? 'theme-switcher__item--active' : ''}`.trim()}
|
|
79
|
+
onClick={() => apply(t.value)}
|
|
80
|
+
>
|
|
81
|
+
{t.label}
|
|
82
|
+
</button>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default ThemeSwitcher;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { Alert } from './Alert';
|
|
3
|
+
|
|
4
|
+
export type ToastPosition =
|
|
5
|
+
| 'top-right'
|
|
6
|
+
| 'top-left'
|
|
7
|
+
| 'bottom-right'
|
|
8
|
+
| 'bottom-left'
|
|
9
|
+
| 'top-center'
|
|
10
|
+
| 'bottom-center';
|
|
11
|
+
|
|
12
|
+
export interface ToastProps extends HTMLAttributes<HTMLDivElement> {
|
|
13
|
+
variant?: 'success' | 'error' | 'warning' | 'info';
|
|
14
|
+
dismissible?: boolean;
|
|
15
|
+
autoDismiss?: number;
|
|
16
|
+
position?: ToastPosition;
|
|
17
|
+
children?: ReactNode;
|
|
18
|
+
className?: string;
|
|
19
|
+
id?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function Toast({
|
|
23
|
+
variant = 'info',
|
|
24
|
+
dismissible = true,
|
|
25
|
+
autoDismiss = 5000,
|
|
26
|
+
position = 'top-right',
|
|
27
|
+
children,
|
|
28
|
+
className = '',
|
|
29
|
+
id,
|
|
30
|
+
...rest
|
|
31
|
+
}: ToastProps) {
|
|
32
|
+
const positionClass = `toast--${position}`;
|
|
33
|
+
const classes = ['toast', positionClass, className].filter(Boolean).join(' ').trim();
|
|
34
|
+
return (
|
|
35
|
+
<div className={classes} data-toast-container {...rest}>
|
|
36
|
+
<Alert variant={variant} dismissible={dismissible} autoDismiss={autoDismiss} id={id}>
|
|
37
|
+
{children}
|
|
38
|
+
</Alert>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default Toast;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface ToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
|
|
4
|
+
pressed?: boolean;
|
|
5
|
+
onPressedChange?: (pressed: boolean) => void;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Toggle({
|
|
11
|
+
pressed = false,
|
|
12
|
+
onPressedChange,
|
|
13
|
+
disabled = false,
|
|
14
|
+
type = 'button',
|
|
15
|
+
value,
|
|
16
|
+
'aria-label': ariaLabel,
|
|
17
|
+
className = '',
|
|
18
|
+
children,
|
|
19
|
+
onClick,
|
|
20
|
+
...rest
|
|
21
|
+
}: ToggleProps) {
|
|
22
|
+
const pressedClass = pressed ? 'toggle--pressed' : '';
|
|
23
|
+
const classes = ['toggle', pressedClass, className].filter(Boolean).join(' ').trim();
|
|
24
|
+
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
25
|
+
onClick?.(e);
|
|
26
|
+
if (!disabled) onPressedChange?.(!pressed);
|
|
27
|
+
};
|
|
28
|
+
return (
|
|
29
|
+
<button
|
|
30
|
+
type={type}
|
|
31
|
+
className={classes}
|
|
32
|
+
aria-pressed={pressed}
|
|
33
|
+
disabled={disabled}
|
|
34
|
+
value={value}
|
|
35
|
+
aria-label={ariaLabel}
|
|
36
|
+
data-toggle
|
|
37
|
+
onClick={handleClick}
|
|
38
|
+
{...rest}
|
|
39
|
+
>
|
|
40
|
+
{children}
|
|
41
|
+
</button>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default Toggle;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface ToggleGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
type?: 'single' | 'multiple';
|
|
5
|
+
orientation?: 'horizontal' | 'vertical';
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function ToggleGroup({
|
|
11
|
+
type = 'single',
|
|
12
|
+
orientation = 'horizontal',
|
|
13
|
+
className = '',
|
|
14
|
+
children,
|
|
15
|
+
...rest
|
|
16
|
+
}: ToggleGroupProps) {
|
|
17
|
+
const orientationClass = orientation === 'vertical' ? 'toggle-group--vertical' : '';
|
|
18
|
+
const role = type === 'single' ? 'radiogroup' : 'group';
|
|
19
|
+
const classes = ['toggle-group', orientationClass, className].filter(Boolean).join(' ').trim();
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className={classes}
|
|
23
|
+
role={role}
|
|
24
|
+
aria-label="Toggle group"
|
|
25
|
+
data-toggle-group
|
|
26
|
+
data-toggle-type={type}
|
|
27
|
+
{...rest}
|
|
28
|
+
>
|
|
29
|
+
{children}
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default ToggleGroup;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
export type TooltipPosition = 'top' | 'bottom' | 'left' | 'right';
|
|
4
|
+
|
|
5
|
+
export interface TooltipProps extends HTMLAttributes<HTMLSpanElement> {
|
|
6
|
+
text: string;
|
|
7
|
+
position?: TooltipPosition;
|
|
8
|
+
delay?: number;
|
|
9
|
+
className?: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Tooltip({
|
|
14
|
+
text,
|
|
15
|
+
position = 'top',
|
|
16
|
+
delay = 0,
|
|
17
|
+
className = '',
|
|
18
|
+
id: idProp,
|
|
19
|
+
style,
|
|
20
|
+
...rest
|
|
21
|
+
}: TooltipProps) {
|
|
22
|
+
const tooltipId = idProp ?? `tooltip-${Math.random().toString(36).slice(2, 11)}`;
|
|
23
|
+
const positionClass = `tooltip--${position}`;
|
|
24
|
+
const classes = ['tooltip', positionClass, className].filter(Boolean).join(' ').trim();
|
|
25
|
+
const delayStyle = delay > 0 ? { ['--tooltip-delay' as string]: `${delay}ms`, ...(style as object) } : style;
|
|
26
|
+
return (
|
|
27
|
+
<span
|
|
28
|
+
className={classes}
|
|
29
|
+
role="tooltip"
|
|
30
|
+
id={tooltipId}
|
|
31
|
+
aria-hidden="true"
|
|
32
|
+
style={delayStyle}
|
|
33
|
+
{...rest}
|
|
34
|
+
>
|
|
35
|
+
{text}
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default Tooltip;
|
|
@@ -28,7 +28,7 @@ If you prefer to load CSS from a CDN instead of the local file, replace the `<li
|
|
|
28
28
|
- `<link rel="stylesheet" href="https://unpkg.com/rizzo-css@latest/dist/rizzo.min.css" />`
|
|
29
29
|
- Or jsDelivr: `https://cdn.jsdelivr.net/npm/rizzo-css@latest/dist/rizzo.min.css`
|
|
30
30
|
|
|
31
|
-
(Replace `@latest` with a specific version, e.g. `@0.0.
|
|
31
|
+
(Replace `@latest` with a specific version, e.g. `@0.0.63`, in production.)
|
|
32
32
|
|
|
33
33
|
The CLI replaces placeholders in `index.html` (e.g. `{{DATA_THEME}}`, `{{TITLE}}`) when you run `rizzo-css init`. The theme selected during init is used on first load when you have no saved preference in the browser.
|
|
34
34
|
|
|
@@ -397,6 +397,26 @@
|
|
|
397
397
|
|
|
398
398
|
|
|
399
399
|
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
400
420
|
|
|
401
421
|
|
|
402
422
|
|
|
@@ -649,6 +669,26 @@
|
|
|
649
669
|
|
|
650
670
|
|
|
651
671
|
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
652
692
|
|
|
653
693
|
|
|
654
694
|
|
|
@@ -397,6 +397,26 @@
|
|
|
397
397
|
|
|
398
398
|
|
|
399
399
|
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
400
420
|
|
|
401
421
|
|
|
402
422
|
|
|
@@ -649,6 +669,26 @@
|
|
|
649
669
|
|
|
650
670
|
|
|
651
671
|
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
652
692
|
|
|
653
693
|
|
|
654
694
|
|
|
@@ -397,6 +397,26 @@
|
|
|
397
397
|
|
|
398
398
|
|
|
399
399
|
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
400
420
|
|
|
401
421
|
|
|
402
422
|
|
|
@@ -649,6 +669,26 @@
|
|
|
649
669
|
|
|
650
670
|
|
|
651
671
|
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
652
692
|
|
|
653
693
|
|
|
654
694
|
|
|
@@ -397,6 +397,26 @@
|
|
|
397
397
|
|
|
398
398
|
|
|
399
399
|
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
400
420
|
|
|
401
421
|
|
|
402
422
|
|
|
@@ -649,6 +669,26 @@
|
|
|
649
669
|
|
|
650
670
|
|
|
651
671
|
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
652
692
|
|
|
653
693
|
|
|
654
694
|
|