pixelize-design-library 2.4.2-beta.44 → 2.4.2-beta.45

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/COMPONENT-GOTCHAS.md +62 -1
  3. package/dist/Components/Common/fieldStyles.d.ts +1 -1
  4. package/dist/Components/Common/fieldStyles.js +4 -4
  5. package/dist/Components/DatePicker/CalendarPanel.js +2 -5
  6. package/dist/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtp.js +2 -4
  7. package/dist/Components/FilterSidebar/FilterSidebar.js +2 -1
  8. package/dist/Components/Header/HeaderActions.d.ts +2 -0
  9. package/dist/Components/Header/HeaderActions.js +2 -2
  10. package/dist/Components/Input/TextInput.styles.js +1 -1
  11. package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.js +1 -1
  12. package/dist/Components/KanbanBoard/KanbanBoardProps.d.ts +2 -0
  13. package/dist/Components/MultiSelect/MultiSelect.d.ts +1 -1
  14. package/dist/Components/MultiSelect/MultiSelect.js +23 -12
  15. package/dist/Components/MultiSelect/MultiSelectProps.d.ts +2 -0
  16. package/dist/Components/NotificationBell/notificationHelpers.d.ts +5 -2
  17. package/dist/Components/NotificationBell/notificationHelpers.js +13 -4
  18. package/dist/Components/PinInput/PinInput.js +2 -4
  19. package/dist/Components/SearchSelect/SearchSelect.js +1 -1
  20. package/dist/Components/SegmentedControl/SegmentedControl.d.ts +1 -1
  21. package/dist/Components/SegmentedControl/SegmentedControl.js +31 -16
  22. package/dist/Components/SegmentedControl/SegmentedControlProps.d.ts +2 -0
  23. package/dist/Components/SegmentedControl/segmentedInk.d.ts +11 -0
  24. package/dist/Components/SegmentedControl/segmentedInk.js +30 -0
  25. package/dist/Components/Select/SelectPopup.js +11 -3
  26. package/dist/Components/SideBar/SecondaryBar.js +5 -4
  27. package/dist/Components/SideBar/SideBar.js +6 -5
  28. package/dist/Components/SideBar/SideBarProps.d.ts +2 -0
  29. package/dist/Components/SideBar/components/MenuItemBox.js +5 -4
  30. package/dist/Components/SideBar/components/MenuItems.js +1 -1
  31. package/dist/Components/SideBar/components/MenuPopoverContent.js +4 -3
  32. package/dist/Components/SideBar/sidebarScale.d.ts +15 -0
  33. package/dist/Components/SideBar/sidebarScale.js +18 -0
  34. package/dist/Components/Switch/Switch.d.ts +2 -2
  35. package/dist/Components/Switch/Switch.js +3 -3
  36. package/dist/Components/Switch/SwitchProps.d.ts +2 -0
  37. package/dist/Components/Table/components/Pagination.d.ts +1 -1
  38. package/dist/Components/Table/components/Pagination.js +12 -4
  39. package/dist/Components/Table/filters/SelectOperationControls.js +9 -1
  40. package/dist/Components/Table/filters/TextOperationControls.js +8 -1
  41. package/dist/Components/Table/hooks/useTable.d.ts +2 -1
  42. package/dist/Components/Table/hooks/useTable.js +5 -3
  43. package/dist/Components/Tabs/components/Tab.js +11 -1
  44. package/dist/Theme/chakra/focusRing.styles.d.ts +17 -1
  45. package/dist/Theme/chakra/focusRing.styles.js +16 -1
  46. package/dist/Theme/provider/ThemeSwitcher.js +15 -8
  47. package/dist/esm/Components/Common/fieldStyles.js +5 -5
  48. package/dist/esm/Components/DatePicker/CalendarPanel.js +2 -5
  49. package/dist/esm/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtp.js +2 -4
  50. package/dist/esm/Components/FilterSidebar/FilterSidebar.js +2 -1
  51. package/dist/esm/Components/Header/HeaderActions.js +2 -2
  52. package/dist/esm/Components/Input/TextInput.styles.js +2 -2
  53. package/dist/esm/Components/KanbanBoard/KanbanActions/KanbanActions.js +1 -1
  54. package/dist/esm/Components/MultiSelect/MultiSelect.js +23 -12
  55. package/dist/esm/Components/NotificationBell/notificationHelpers.js +13 -4
  56. package/dist/esm/Components/PinInput/PinInput.js +2 -4
  57. package/dist/esm/Components/SearchSelect/SearchSelect.js +1 -1
  58. package/dist/esm/Components/SegmentedControl/SegmentedControl.js +30 -15
  59. package/dist/esm/Components/SegmentedControl/segmentedInk.js +23 -0
  60. package/dist/esm/Components/Select/SelectPopup.js +11 -3
  61. package/dist/esm/Components/SideBar/SecondaryBar.js +5 -4
  62. package/dist/esm/Components/SideBar/SideBar.js +6 -5
  63. package/dist/esm/Components/SideBar/components/MenuItemBox.js +5 -4
  64. package/dist/esm/Components/SideBar/components/MenuItems.js +1 -1
  65. package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +4 -3
  66. package/dist/esm/Components/SideBar/sidebarScale.js +15 -0
  67. package/dist/esm/Components/Switch/Switch.js +3 -3
  68. package/dist/esm/Components/Table/components/Pagination.js +13 -5
  69. package/dist/esm/Components/Table/filters/SelectOperationControls.js +8 -3
  70. package/dist/esm/Components/Table/filters/TextOperationControls.js +6 -2
  71. package/dist/esm/Components/Table/hooks/useTable.js +4 -3
  72. package/dist/esm/Components/Tabs/components/Tab.js +12 -2
  73. package/dist/esm/Theme/chakra/focusRing.styles.js +13 -0
  74. package/dist/esm/Theme/provider/ThemeSwitcher.js +16 -9
  75. package/package.json +1 -1
@@ -14,6 +14,19 @@ export const focusVisibleRing = (colors) => ({
14
14
  outlineOffset: "2px",
15
15
  boxShadow: focusRingShadow(colors),
16
16
  });
17
+ /** Blurred, never a flat band: a band has a hard outer edge at every opacity and reads as a second border. */
18
+ export const FOCUS_GLOW_BLUR = 6;
19
+ /** Negative spread keeps the bloom tight to the border instead of haloing the whole control. */
20
+ export const FOCUS_GLOW_SPREAD = -1;
21
+ /** Step on `primary.opacity`. Safe to raise because a blur has no edge to sharpen. */
22
+ export const FOCUS_GLOW_ALPHA = 32;
23
+ export const focusGlowShadow = (colors) => `0 0 ${FOCUS_GLOW_BLUR}px ${FOCUS_GLOW_SPREAD}px ${colors.primary.opacity[FOCUS_GLOW_ALPHA]}`;
24
+ /** Field focus: the brand border is the indicator, this only makes it findable. */
25
+ export const fieldFocusVisible = (colors) => ({
26
+ outline: "2px solid transparent",
27
+ outlineOffset: "2px",
28
+ boxShadow: focusGlowShadow(colors),
29
+ });
17
30
  /** Both selectors a control needs to keep the ring on top of its own hover/active styling. */
18
31
  export const focusVisibleStyles = (colors) => {
19
32
  const ring = focusVisibleRing(colors);
@@ -1,10 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useRef } from "react";
2
+ import { useRef, useState } from "react";
3
3
  import { Box, Button, HStack, IconButton, Menu, MenuButton, MenuDivider, MenuItemOption, MenuList, MenuOptionGroup, Text, Tooltip, } from "@chakra-ui/react";
4
4
  import { Monitor, Moon, Sun } from "lucide-react";
5
5
  import { brandThemes } from "../index.js";
6
6
  import { useCustomTheme } from "../useCustomTheme.js";
7
7
  import { useThemeMode } from "./useThemeMode.js";
8
+ import { focusGlowShadow, focusVisibleRing } from "../chakra/focusRing.styles.js";
9
+ import { AA_NON_TEXT, liftToContrast } from "../tokens/builders/outlineRung.js";
8
10
  const MODE_OPTIONS = [
9
11
  { value: "light", label: "Light", Icon: Sun },
10
12
  { value: "system", label: "System", Icon: Monitor },
@@ -34,25 +36,30 @@ const useSwitcherColors = () => {
34
36
  borderColor: colors.boxborder[200],
35
37
  // NOTE: menuBg lives in Menu.styles.ts — re-pushing it via `sx` flickers on every render.
36
38
  menuColor: colors.text[900],
39
+ focusRing: focusVisibleRing(colors),
37
40
  };
38
41
  };
39
42
  const BrandMenu = ({ size }) => {
40
43
  const { brand, setBrand } = useThemeMode();
41
- const { mutedColor, menuColor } = useSwitcherColors();
44
+ const { mutedColor, menuColor, focusRing } = useSwitcherColors();
42
45
  const brands = Object.keys(brandThemes);
43
- return (_jsxs(Menu, { children: [_jsx(MenuButton, { as: Button, size: SIZES[size].control, variant: "ghost", color: mutedColor, borderRadius: "full", fontWeight: 500, "aria-label": `Brand theme, current ${toBrandLabel(brand)}`, _focusVisible: { boxShadow: "outline" }, children: _jsxs(HStack, { spacing: 2, children: [_jsx(BrandSwatch, { brand: brand }), _jsx(Text, { as: "span", children: toBrandLabel(brand) })] }) }), _jsx(MenuList, { minW: "9rem", color: menuColor, children: _jsx(MenuOptionGroup, { title: "Brand", type: "radio", value: brand, onChange: (value) => setBrand(value), children: brands.map((option) => (_jsx(MenuItemOption, { value: option, children: _jsxs(HStack, { spacing: 2, children: [_jsx(BrandSwatch, { brand: option }), _jsx(Text, { as: "span", children: toBrandLabel(option) })] }) }, option))) }) })] }));
46
+ return (_jsxs(Menu, { children: [_jsx(MenuButton, { as: Button, size: SIZES[size].control, variant: "ghost", color: mutedColor, borderRadius: "full", fontWeight: 500, "aria-label": `Brand theme, current ${toBrandLabel(brand)}`, _focusVisible: focusRing, children: _jsxs(HStack, { spacing: 2, children: [_jsx(BrandSwatch, { brand: brand }), _jsx(Text, { as: "span", children: toBrandLabel(brand) })] }) }), _jsx(MenuList, { minW: "9rem", color: menuColor, children: _jsx(MenuOptionGroup, { title: "Brand", type: "radio", value: brand, onChange: (value) => setBrand(value), children: brands.map((option) => (_jsx(MenuItemOption, { value: option, children: _jsxs(HStack, { spacing: 2, children: [_jsx(BrandSwatch, { brand: option }), _jsx(Text, { as: "span", children: toBrandLabel(option) })] }) }, option))) }) })] }));
44
47
  };
45
48
  const IconVariant = ({ size, label, }) => {
46
49
  const { resolvedMode, toggle } = useThemeMode();
47
- const { mutedColor, hoverBg } = useSwitcherColors();
50
+ const { mutedColor, hoverBg, focusRing } = useSwitcherColors();
48
51
  const isDark = resolvedMode === "dark";
49
52
  const Icon = isDark ? Moon : Sun;
50
53
  const accessibleLabel = label !== null && label !== void 0 ? label : `Switch to ${isDark ? "light" : "dark"} mode`;
51
- return (_jsx(Tooltip, { label: accessibleLabel, openDelay: 400, children: _jsx(IconButton, { "aria-label": accessibleLabel, "aria-pressed": isDark, icon: _jsx(Icon, { size: SIZES[size].icon, "aria-hidden": true }), onClick: toggle, size: SIZES[size].control, variant: "ghost", color: mutedColor, borderRadius: "full", _hover: { bg: hoverBg }, _focusVisible: { boxShadow: "outline" } }) }));
54
+ return (_jsx(Tooltip, { label: accessibleLabel, openDelay: 400, children: _jsx(IconButton, { "aria-label": accessibleLabel, "aria-pressed": isDark, icon: _jsx(Icon, { size: SIZES[size].icon, "aria-hidden": true }), onClick: toggle, size: SIZES[size].control, variant: "ghost", color: mutedColor, borderRadius: "full", _hover: { bg: hoverBg }, _focusVisible: focusRing }) }));
52
55
  };
53
56
  const SegmentedVariant = ({ size, label, showBrandPicker, }) => {
54
57
  const { mode, setMode } = useThemeMode();
55
58
  const { trackBg, activeBg, activeColor, mutedColor, borderColor } = useSwitcherColors();
59
+ const { colors } = useCustomTheme();
60
+ // One tab stop, so the group wears the indicator: brand border plus the field bloom.
61
+ const focusBorder = liftToContrast(colors.primary[500], trackBg, AA_NON_TEXT);
62
+ const [showFocus, setShowFocus] = useState(false);
56
63
  const optionRefs = useRef([]);
57
64
  // APG radiogroup behavior: arrows move focus and selection together.
58
65
  const handleKeyDown = (event, index) => {
@@ -66,20 +73,20 @@ const SegmentedVariant = ({ size, label, showBrandPicker, }) => {
66
73
  setMode(MODE_OPTIONS[next].value);
67
74
  (_a = optionRefs.current[next]) === null || _a === void 0 ? void 0 : _a.focus();
68
75
  };
69
- return (_jsxs(HStack, { role: "radiogroup", "aria-label": label !== null && label !== void 0 ? label : "Color mode", display: "inline-flex", spacing: 1, p: "0.25rem", bg: trackBg, border: "0.063rem solid", borderColor: borderColor, borderRadius: "full", children: [MODE_OPTIONS.map(({ value, label: optionLabel, Icon }, index) => {
76
+ return (_jsxs(HStack, { role: "radiogroup", "aria-label": label !== null && label !== void 0 ? label : "Color mode", display: "inline-flex", spacing: 1, p: "0.25rem", bg: trackBg, border: "0.063rem solid", borderColor: showFocus ? focusBorder : borderColor, boxShadow: showFocus ? focusGlowShadow(colors) : undefined, borderRadius: "full", onFocus: (event) => setShowFocus(event.target.matches(":focus-visible")), onBlur: () => setShowFocus(false), children: [MODE_OPTIONS.map(({ value, label: optionLabel, Icon }, index) => {
70
77
  const isSelected = mode === value;
71
78
  return (_jsx(Button, { ref: (node) => {
72
79
  optionRefs.current[index] = node;
73
- }, role: "radio", "aria-checked": isSelected, "aria-label": optionLabel, onClick: () => setMode(value), onKeyDown: (event) => handleKeyDown(event, index), size: SIZES[size].control, variant: "ghost", borderRadius: "full", bg: isSelected ? activeBg : "transparent", color: isSelected ? activeColor : mutedColor, boxShadow: isSelected ? "sm" : "none", _hover: { color: activeColor }, _focusVisible: { boxShadow: "outline" }, children: _jsx(Icon, { size: SIZES[size].icon, "aria-hidden": true }) }, value));
80
+ }, role: "radio", "aria-checked": isSelected, "aria-label": optionLabel, onClick: () => setMode(value), onKeyDown: (event) => handleKeyDown(event, index), size: SIZES[size].control, variant: "ghost", borderRadius: "full", bg: isSelected ? activeBg : "transparent", color: isSelected ? activeColor : mutedColor, boxShadow: isSelected ? "sm" : "none", _hover: { color: activeColor }, _focusVisible: { outline: "2px solid transparent", outlineOffset: "2px" }, children: _jsx(Icon, { size: SIZES[size].icon, "aria-hidden": true }) }, value));
74
81
  }), showBrandPicker && _jsx(BrandMenu, { size: size })] }));
75
82
  };
76
83
  const MenuVariant = ({ size, label, showBrandPicker, }) => {
77
84
  var _a;
78
85
  const { mode, brand, setMode, setBrand } = useThemeMode();
79
- const { mutedColor, hoverBg, menuColor } = useSwitcherColors();
86
+ const { mutedColor, hoverBg, menuColor, focusRing } = useSwitcherColors();
80
87
  const active = (_a = MODE_OPTIONS.find((option) => option.value === mode)) !== null && _a !== void 0 ? _a : MODE_OPTIONS[0];
81
88
  const brands = Object.keys(brandThemes);
82
- return (_jsxs(Menu, { children: [_jsx(MenuButton, { as: IconButton, "aria-label": label !== null && label !== void 0 ? label : `Color mode, current ${active.label}`, icon: _jsx(active.Icon, { size: SIZES[size].icon, "aria-hidden": true }), size: SIZES[size].control, variant: "ghost", color: mutedColor, borderRadius: "full", _hover: { bg: hoverBg }, _focusVisible: { boxShadow: "outline" } }), _jsxs(MenuList, { minW: "10rem", color: menuColor, children: [_jsx(MenuOptionGroup, { title: "Appearance", type: "radio", value: mode, onChange: (value) => setMode(value), children: MODE_OPTIONS.map(({ value, label: optionLabel, Icon }) => (_jsx(MenuItemOption, { value: value, children: _jsxs(HStack, { spacing: 2, children: [_jsx(Icon, { size: SIZES[size].icon, "aria-hidden": true }), _jsx(Text, { as: "span", children: optionLabel })] }) }, value))) }), showBrandPicker && (_jsxs(_Fragment, { children: [_jsx(MenuDivider, {}), _jsx(MenuOptionGroup, { title: "Brand", type: "radio", value: brand, onChange: (value) => setBrand(value), children: brands.map((option) => (_jsx(MenuItemOption, { value: option, children: _jsxs(HStack, { spacing: 2, children: [_jsx(BrandSwatch, { brand: option }), _jsx(Text, { as: "span", children: toBrandLabel(option) })] }) }, option))) })] }))] })] }));
89
+ return (_jsxs(Menu, { children: [_jsx(MenuButton, { as: IconButton, "aria-label": label !== null && label !== void 0 ? label : `Color mode, current ${active.label}`, icon: _jsx(active.Icon, { size: SIZES[size].icon, "aria-hidden": true }), size: SIZES[size].control, variant: "ghost", color: mutedColor, borderRadius: "full", _hover: { bg: hoverBg }, _focusVisible: focusRing }), _jsxs(MenuList, { minW: "10rem", color: menuColor, children: [_jsx(MenuOptionGroup, { title: "Appearance", type: "radio", value: mode, onChange: (value) => setMode(value), children: MODE_OPTIONS.map(({ value, label: optionLabel, Icon }) => (_jsx(MenuItemOption, { value: value, children: _jsxs(HStack, { spacing: 2, children: [_jsx(Icon, { size: SIZES[size].icon, "aria-hidden": true }), _jsx(Text, { as: "span", children: optionLabel })] }) }, value))) }), showBrandPicker && (_jsxs(_Fragment, { children: [_jsx(MenuDivider, {}), _jsx(MenuOptionGroup, { title: "Brand", type: "radio", value: brand, onChange: (value) => setBrand(value), children: brands.map((option) => (_jsx(MenuItemOption, { value: option, children: _jsxs(HStack, { spacing: 2, children: [_jsx(BrandSwatch, { brand: option }), _jsx(Text, { as: "span", children: toBrandLabel(option) })] }) }, option))) })] }))] })] }));
83
90
  };
84
91
  export const ThemeSwitcher = ({ variant = "icon", showBrandPicker = false, size = "md", "aria-label": ariaLabel, }) => {
85
92
  if (variant === "segmented") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelize-design-library",
3
- "version": "2.4.2-beta.44",
3
+ "version": "2.4.2-beta.45",
4
4
  "description": "React component library for Pixelize apps: themeable Chakra-based components, design tokens and light/dark brand theming.",
5
5
  "keywords": [
6
6
  "react",