@tangible/ui 0.0.5 → 0.0.7
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 +124 -27
- package/components/Accordion/Accordion.js +1 -1
- package/components/Combobox/Combobox.d.ts +1 -1
- package/components/Combobox/Combobox.js +4 -3
- package/components/Combobox/types.d.ts +5 -0
- package/components/Field/Field.js +14 -4
- package/components/Field/FieldContext.d.ts +2 -0
- package/components/Icon/Icon.js +2 -1
- package/components/Modal/Modal.js +2 -2
- package/components/MoveHandle/MoveHandle.js +13 -2
- package/components/MultiSelect/MultiSelect.js +2 -1
- package/components/Progress/Progress.js +2 -1
- package/components/Radio/Radio.d.ts +4 -0
- package/components/Radio/Radio.js +15 -5
- package/components/Radio/RadioGroup.d.ts +1 -1
- package/components/Radio/RadioGroup.js +2 -2
- package/components/Radio/types.d.ts +10 -0
- package/components/Select/Select.js +2 -1
- package/components/StepList/StepList.js +2 -1
- package/components/Switch/Switch.js +28 -14
- package/components/Tabs/Tabs.js +2 -2
- package/components/Toolbar/Toolbar.js +2 -1
- package/components/Tooltip/Tooltip.js +2 -1
- package/package.json +7 -9
- package/styles/all.css +1 -1
- package/styles/all.expanded.css +223 -109
- package/styles/all.expanded.unlayered.css +223 -109
- package/styles/all.unlayered.css +1 -1
- package/styles/components/input/index.scss +2 -2
- package/styles/index.scss +14 -0
- package/styles/system/_control.scss +6 -3
- package/styles/utilities/_index.scss +14 -4
- package/tui-manifest.json +39 -4
- package/utils/use-roving-group.js +9 -6
|
@@ -8,18 +8,13 @@ import { isDev } from '../../utils/is-dev.js';
|
|
|
8
8
|
// Switch Component
|
|
9
9
|
// =============================================================================
|
|
10
10
|
//
|
|
11
|
-
// <button role="switch"> with animated thumb. Uses
|
|
12
|
-
// Existing tui-toggle CSS untouched (stays for CSS-only usage).
|
|
11
|
+
// <button role="switch"> with animated thumb. Uses tui-switch SCSS.
|
|
13
12
|
//
|
|
14
13
|
// Bare (no label): returns <button> directly for Field.Control cloneElement.
|
|
15
|
-
// With label: wraps in <label>,
|
|
16
|
-
//
|
|
17
|
-
// CSS token API:
|
|
18
|
-
// --tui-switch-accent Accent color for on state
|
|
19
|
-
// --tui-switch-track-off Track color when off
|
|
14
|
+
// With label: wraps in <label>, label-text clicks forward to button.
|
|
20
15
|
//
|
|
21
16
|
// =============================================================================
|
|
22
|
-
// Props that should route to the <button>, not the wrapper
|
|
17
|
+
// Props that should route to the <button>, not the wrapper.
|
|
23
18
|
const BUTTON_PROPS = new Set([
|
|
24
19
|
'id',
|
|
25
20
|
'aria-describedby',
|
|
@@ -29,6 +24,7 @@ const BUTTON_PROPS = new Set([
|
|
|
29
24
|
'aria-labelledby',
|
|
30
25
|
'form',
|
|
31
26
|
'tabIndex',
|
|
27
|
+
'onClick',
|
|
32
28
|
'onFocus',
|
|
33
29
|
'onBlur',
|
|
34
30
|
]);
|
|
@@ -57,13 +53,18 @@ export const Switch = forwardRef(function Switch({ checked: controlledChecked, d
|
|
|
57
53
|
// Extract onClick from rest so prop spreading can't override internal handler
|
|
58
54
|
const { onClick: onClickProp, ...restWithoutClick } = rest;
|
|
59
55
|
const handleClick = (e) => {
|
|
56
|
+
if (disabled)
|
|
57
|
+
return;
|
|
60
58
|
setChecked((prev) => !prev);
|
|
61
59
|
onClickProp?.(e);
|
|
62
60
|
};
|
|
63
61
|
const isChecked = checked ?? false;
|
|
62
|
+
// Shared button element — single source of truth for both render paths.
|
|
63
|
+
// In labeled mode, buttonProps/sizeClass are set differently (see below).
|
|
64
|
+
const renderButton = (extraProps, extraClass) => (_jsx("button", { ref: composeRefs(internalRef, externalRef), type: "button", role: "switch", "aria-checked": isChecked, disabled: disabled, className: cx('tui-switch__track', extraClass), onClick: handleClick, ...extraProps, children: _jsx("span", { className: "tui-switch__thumb" }) }));
|
|
64
65
|
// Bare: no label — Field.Control can inject id/aria-* directly
|
|
65
66
|
if (!label) {
|
|
66
|
-
return (
|
|
67
|
+
return renderButton(restWithoutClick, cx(sizeClass, className));
|
|
67
68
|
}
|
|
68
69
|
// Split rest props: some go on button, some on wrapper
|
|
69
70
|
const buttonProps = {};
|
|
@@ -76,16 +77,29 @@ export const Switch = forwardRef(function Switch({ checked: controlledChecked, d
|
|
|
76
77
|
wrapperProps[key] = val;
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
// DEV: warn if button-like props leaked to the wrapper
|
|
81
|
+
if (isDev()) {
|
|
82
|
+
const suspect = Object.keys(wrapperProps).filter((k) => k.startsWith('aria-') || k.startsWith('on') || k === 'tabIndex');
|
|
83
|
+
if (suspect.length > 0) {
|
|
84
|
+
console.warn(`Switch: Props [${suspect.join(', ')}] ended up on the <label> wrapper. ` +
|
|
85
|
+
'Add them to BUTTON_PROPS if they belong on the <button>.');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// <label> wrapping a <button> causes native click-forwarding (button IS
|
|
89
|
+
// a labelable element). Without preventDefault, clicking the button fires
|
|
90
|
+
// handleClick AND the forwarded click — double-toggle.
|
|
91
|
+
// We suppress native forwarding. Clicks on the button are handled by its
|
|
92
|
+
// own onClick (handleClick). Clicks on the label text toggle state here.
|
|
82
93
|
const handleLabelClick = (e) => {
|
|
83
|
-
// Prevent native label click from also activating the button
|
|
84
94
|
e.preventDefault();
|
|
95
|
+
// Clicks on/inside the button are handled by handleClick (via bubbling)
|
|
96
|
+
if (internalRef.current?.contains(e.target))
|
|
97
|
+
return;
|
|
85
98
|
if (disabled)
|
|
86
99
|
return;
|
|
87
100
|
internalRef.current?.focus();
|
|
88
101
|
setChecked((prev) => !prev);
|
|
89
102
|
};
|
|
90
|
-
return (_jsxs("label", { className: cx('tui-switch', sizeClass, disabled && 'is-disabled', className), onClick: handleLabelClick, ...wrapperProps, children: [
|
|
103
|
+
return (_jsxs("label", { className: cx('tui-switch', sizeClass, disabled && 'is-disabled', className), onClick: handleLabelClick, ...wrapperProps, children: [renderButton(buttonProps), _jsx("span", { className: "tui-switch__label", children: label })] }));
|
|
91
104
|
});
|
|
105
|
+
Switch.displayName = 'Switch';
|
package/components/Tabs/Tabs.js
CHANGED
|
@@ -104,7 +104,7 @@ function TabsRoot({ variant = 'underline', activationMode = 'auto', value: contr
|
|
|
104
104
|
}, [registryVersion, activeValue, focusedValue, isControlled, onValueChange, getOrderedTabs]);
|
|
105
105
|
// Dev-only: Tab-Panel pairing validation
|
|
106
106
|
useEffect(() => {
|
|
107
|
-
if (
|
|
107
|
+
if (isDev()) {
|
|
108
108
|
// Defer validation to next microtask so all tabs/panels have registered
|
|
109
109
|
queueMicrotask(() => {
|
|
110
110
|
const tabValues = new Set(Array.from(tabsRef.current.keys()));
|
|
@@ -167,7 +167,7 @@ function TabsList({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy,
|
|
|
167
167
|
const { orientation, activationMode, activeValue, focusedValue, getOrderedTabs, onSelect, setFocusedValue, tabsRef, } = useTabsContext();
|
|
168
168
|
// Dev-only: Warn if missing accessible name
|
|
169
169
|
useEffect(() => {
|
|
170
|
-
if (
|
|
170
|
+
if (isDev()) {
|
|
171
171
|
if (!ariaLabel && !ariaLabelledBy) {
|
|
172
172
|
console.warn('Tabs.List: Missing accessible name. Provide aria-label or aria-labelledby.');
|
|
173
173
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useCallback, useEffect, useRef, cloneElement, isValidElement, } from 'react';
|
|
3
3
|
import { cx } from '../../utils/cx.js';
|
|
4
|
+
import { isDev } from '../../utils/is-dev.js';
|
|
4
5
|
// =============================================================================
|
|
5
6
|
// Toolbar Component
|
|
6
7
|
// =============================================================================
|
|
@@ -66,7 +67,7 @@ function ToolbarRoot({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledB
|
|
|
66
67
|
const originalTabIndexMapRef = useRef(new WeakMap());
|
|
67
68
|
// Dev warning for missing accessible name
|
|
68
69
|
useEffect(() => {
|
|
69
|
-
if (
|
|
70
|
+
if (isDev() && !ariaLabel && !ariaLabelledBy) {
|
|
70
71
|
console.warn('[Toolbar] aria-label or aria-labelledby is required for accessibility.');
|
|
71
72
|
}
|
|
72
73
|
}, [ariaLabel, ariaLabelledBy]);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useCallback, useEffect, useId, useMemo, useRef, useState, cloneElement, isValidElement, } from 'react';
|
|
3
|
+
import { isDev } from '../../utils/is-dev.js';
|
|
3
4
|
import { useFloating, offset, flip, shift, arrow, autoUpdate, FloatingPortal, FloatingArrow, } from '@floating-ui/react';
|
|
4
5
|
import { cx } from '../../utils/cx.js';
|
|
5
6
|
import { getPortalRootFor } from '../../utils/portal.js';
|
|
@@ -165,7 +166,7 @@ function TooltipContentComponent({ side = 'top', align = 'center', sideOffset =
|
|
|
165
166
|
// Dev warning: tooltips should not contain interactive content (WCAG 1.4.13)
|
|
166
167
|
// Use Popover for interactive overlays instead
|
|
167
168
|
useEffect(() => {
|
|
168
|
-
if (
|
|
169
|
+
if (isDev() && open && refs.floating.current) {
|
|
169
170
|
const interactive = refs.floating.current.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
170
171
|
if (interactive.length > 0) {
|
|
171
172
|
console.warn('[Tooltip] Contains interactive elements which violates WCAG 1.4.13. ' +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangible/ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Tangible Design System",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./components/index.js",
|
|
@@ -79,20 +79,18 @@
|
|
|
79
79
|
"types": "./components/Dropdown/Dropdown.d.ts"
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
|
-
"dependencies": {
|
|
83
|
-
"@floating-ui/react": "^0.27.16",
|
|
84
|
-
"@tanstack/react-table": "^8.21.3"
|
|
85
|
-
},
|
|
86
82
|
"peerDependencies": {
|
|
83
|
+
"@floating-ui/react": ">=0.27.0",
|
|
84
|
+
"@tanstack/react-table": ">=8.0.0",
|
|
87
85
|
"react": ">=18.0.0",
|
|
88
86
|
"react-dom": ">=18.0.0"
|
|
89
87
|
},
|
|
90
88
|
"peerDependenciesMeta": {
|
|
91
|
-
"react": {
|
|
92
|
-
"optional":
|
|
89
|
+
"@floating-ui/react": {
|
|
90
|
+
"optional": true
|
|
93
91
|
},
|
|
94
|
-
"react-
|
|
95
|
-
"optional":
|
|
92
|
+
"@tanstack/react-table": {
|
|
93
|
+
"optional": true
|
|
96
94
|
}
|
|
97
95
|
},
|
|
98
96
|
"homepage": "https://github.com/tangibleinc/tangible-ui",
|