@a-type/ui 5.0.7 → 5.0.9

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 (114) hide show
  1. package/dist/css/main.css +8 -8
  2. package/dist/esm/components/button/Button.js +1 -1
  3. package/dist/esm/components/button/Button.js.map +1 -1
  4. package/dist/esm/components/button/classes.js +2 -2
  5. package/dist/esm/components/button/classes.js.map +1 -1
  6. package/dist/esm/components/camera/Camera.js +1 -1
  7. package/dist/esm/components/camera/Camera.js.map +1 -1
  8. package/dist/esm/components/card/Card.js +3 -3
  9. package/dist/esm/components/card/Card.js.map +1 -1
  10. package/dist/esm/components/checkbox/Checkbox.js +2 -2
  11. package/dist/esm/components/checkbox/Checkbox.js.map +1 -1
  12. package/dist/esm/components/datePicker/Calendar.js +1 -1
  13. package/dist/esm/components/datePicker/Calendar.js.map +1 -1
  14. package/dist/esm/components/datePicker/DateRangePicker.js +1 -1
  15. package/dist/esm/components/datePicker/DateRangePicker.js.map +1 -1
  16. package/dist/esm/components/editableText/EditableText.js +1 -1
  17. package/dist/esm/components/editableText/EditableText.js.map +1 -1
  18. package/dist/esm/components/horizontalList/HorizontalList.js +2 -2
  19. package/dist/esm/components/horizontalList/HorizontalList.js.map +1 -1
  20. package/dist/esm/components/input/Input.js +1 -1
  21. package/dist/esm/components/input/Input.js.map +1 -1
  22. package/dist/esm/components/lightbox/Lightbox.js +1 -1
  23. package/dist/esm/components/lightbox/Lightbox.js.map +1 -1
  24. package/dist/esm/components/navBar/NavBar.js +5 -5
  25. package/dist/esm/components/navBar/NavBar.js.map +1 -1
  26. package/dist/esm/components/note/Note.js +1 -1
  27. package/dist/esm/components/note/Note.js.map +1 -1
  28. package/dist/esm/components/primitives/menus.js +1 -1
  29. package/dist/esm/components/primitives/menus.js.map +1 -1
  30. package/dist/esm/components/progress/Progress.js +5 -5
  31. package/dist/esm/components/progress/Progress.js.map +1 -1
  32. package/dist/esm/components/provider/Provider.d.ts +2 -1
  33. package/dist/esm/components/provider/Provider.js +5 -3
  34. package/dist/esm/components/provider/Provider.js.map +1 -1
  35. package/dist/esm/components/provider/TweakPane.d.ts +1 -0
  36. package/dist/esm/components/provider/TweakPane.js +36 -0
  37. package/dist/esm/components/provider/TweakPane.js.map +1 -0
  38. package/dist/esm/components/scrollArea/ScrollArea.js +5 -5
  39. package/dist/esm/components/scrollArea/ScrollArea.js.map +1 -1
  40. package/dist/esm/components/slider/Slider.d.ts +1 -0
  41. package/dist/esm/components/slider/Slider.js +9 -4
  42. package/dist/esm/components/slider/Slider.js.map +1 -1
  43. package/dist/esm/components/slider/Slider.stories.d.ts +1 -0
  44. package/dist/esm/components/switch/Switch.js +2 -2
  45. package/dist/esm/components/switch/Switch.js.map +1 -1
  46. package/dist/esm/components/tabs/tabs.js +1 -1
  47. package/dist/esm/components/tabs/tabs.js.map +1 -1
  48. package/dist/esm/components/toggleGroup/toggleGroup.js +1 -1
  49. package/dist/esm/components/toggleGroup/toggleGroup.js.map +1 -1
  50. package/dist/esm/components/viewport/Viewport.js +1 -1
  51. package/dist/esm/components/viewport/Viewport.js.map +1 -1
  52. package/dist/esm/hooks/useVisualViewportOffset.d.ts +2 -2
  53. package/dist/esm/hooks/useVisualViewportOffset.js +22 -10
  54. package/dist/esm/hooks/useVisualViewportOffset.js.map +1 -1
  55. package/dist/esm/keyboard.stories.js +3 -3
  56. package/dist/esm/keyboard.stories.js.map +1 -1
  57. package/dist/esm/themes.stories.js +2 -2
  58. package/dist/esm/themes.stories.js.map +1 -1
  59. package/dist/esm/uno/logic/properties.d.ts +6 -0
  60. package/dist/esm/uno/logic/properties.js +6 -0
  61. package/dist/esm/uno/logic/properties.js.map +1 -1
  62. package/dist/esm/uno/preflights/index.d.ts +2 -1
  63. package/dist/esm/uno/preflights/index.js +1 -1
  64. package/dist/esm/uno/preflights/index.js.map +1 -1
  65. package/dist/esm/uno/preflights/keyboard.d.ts +4 -1
  66. package/dist/esm/uno/preflights/keyboard.js +3 -3
  67. package/dist/esm/uno/preflights/keyboard.js.map +1 -1
  68. package/dist/esm/uno/preflights/user.d.ts +2 -1
  69. package/dist/esm/uno/preflights/user.js +8 -1
  70. package/dist/esm/uno/preflights/user.js.map +1 -1
  71. package/dist/esm/uno/rules/focus.d.ts +3 -0
  72. package/dist/esm/uno/rules/focus.js +43 -0
  73. package/dist/esm/uno/rules/focus.js.map +1 -0
  74. package/dist/esm/uno/rules/index.d.ts +1 -1
  75. package/dist/esm/uno/rules/index.js +2 -0
  76. package/dist/esm/uno/rules/index.js.map +1 -1
  77. package/dist/esm/uno/theme/colors.d.ts +1 -1
  78. package/dist/esm/uno/theme/colors.js +3 -3
  79. package/dist/esm/uno/theme/colors.js.map +1 -1
  80. package/package.json +1 -1
  81. package/src/__screenshots__/themes.snapshots.tsx/snapshot-chromium-win32.png +0 -0
  82. package/src/components/button/Button.tsx +1 -1
  83. package/src/components/button/classes.tsx +3 -4
  84. package/src/components/camera/Camera.tsx +5 -5
  85. package/src/components/card/Card.tsx +6 -6
  86. package/src/components/checkbox/Checkbox.tsx +2 -2
  87. package/src/components/datePicker/Calendar.tsx +2 -2
  88. package/src/components/datePicker/DateRangePicker.tsx +3 -3
  89. package/src/components/editableText/EditableText.tsx +2 -1
  90. package/src/components/horizontalList/HorizontalList.tsx +2 -1
  91. package/src/components/input/Input.tsx +2 -2
  92. package/src/components/lightbox/Lightbox.tsx +1 -1
  93. package/src/components/navBar/NavBar.tsx +101 -101
  94. package/src/components/note/Note.tsx +3 -3
  95. package/src/components/primitives/menus.tsx +1 -1
  96. package/src/components/progress/Progress.tsx +101 -101
  97. package/src/components/provider/Provider.tsx +10 -1
  98. package/src/components/provider/TweakPane.tsx +139 -0
  99. package/src/components/scrollArea/ScrollArea.tsx +15 -15
  100. package/src/components/slider/Slider.tsx +103 -97
  101. package/src/components/switch/Switch.tsx +38 -38
  102. package/src/components/tabs/tabs.tsx +2 -1
  103. package/src/components/toggleGroup/toggleGroup.tsx +1 -1
  104. package/src/components/viewport/Viewport.tsx +1 -1
  105. package/src/hooks/useVisualViewportOffset.ts +22 -14
  106. package/src/keyboard.stories.tsx +21 -10
  107. package/src/themes.stories.tsx +2 -0
  108. package/src/uno/logic/properties.ts +6 -0
  109. package/src/uno/preflights/index.ts +4 -3
  110. package/src/uno/preflights/keyboard.ts +15 -8
  111. package/src/uno/preflights/user.ts +13 -0
  112. package/src/uno/rules/focus.ts +46 -0
  113. package/src/uno/rules/index.ts +2 -0
  114. package/src/uno/theme/colors.ts +7 -3
@@ -11,27 +11,27 @@ export type * from '@base-ui/react/scroll-area';
11
11
 
12
12
  export const ScrollAreaRoot = withClassName(
13
13
  BaseScrollArea.Root,
14
- 'layer-components:(min-w-0 min-h-0 flex flex-col box-border)',
14
+ 'layer-components:(box-border min-h-0 min-w-0 flex flex-col)',
15
15
  );
16
16
 
17
17
  export const ScrollAreaViewport = withClassName(
18
18
  BaseScrollArea.Viewport,
19
- 'layer-components:(h-full outline-none min-h-0)',
20
- 'layer-components:focus-visible:(outline-none ring-2 ring-accent)',
19
+ 'layer-components:(h-full min-h-0 outline-none)',
20
+ 'layer-components:foc',
21
21
  );
22
22
 
23
23
  export const ScrollAreaVerticalFades = withClassName(
24
24
  'div',
25
- 'layer-components:(pointer-events-none absolute inset-0 [--scroll-area-overflow-y-start:inherit] [--scroll-area-overflow-y-end:inherit])',
26
- 'layer-components:before:(content-empty [--scroll-area-overflow-y-start:inherit] top-0 block left-0 w-full absolute transition-height h-[min(40px,var(--scroll-area-overflow-y-start,40px))] bg-gradient-to-b bg-gradient-from-bg bg-gradient-to-transparent)',
27
- 'layer-components:after:(content-empty [--scroll-area-overflow-y-end:inherit] bottom-0 block left-0 w-full absolute transition-height h-[min(40px,var(--scroll-area-overflow-y-end,40px))] bg-gradient-to-t bg-gradient-from-bg bg-gradient-to-transparent)',
25
+ 'layer-components:([--scroll-area-overflow-y-end:inherit] [--scroll-area-overflow-y-start:inherit] pointer-events-none absolute inset-0)',
26
+ 'layer-components:before:([--scroll-area-overflow-y-start:inherit] absolute left-0 top-0 block h-[min(40px,var(--scroll-area-overflow-y-start,40px))] w-full bg-gradient-from-bg bg-gradient-to-transparent bg-gradient-to-b transition-height content-empty)',
27
+ 'layer-components:after:([--scroll-area-overflow-y-end:inherit] absolute bottom-0 left-0 block h-[min(40px,var(--scroll-area-overflow-y-end,40px))] w-full bg-gradient-from-bg bg-gradient-to-transparent bg-gradient-to-t transition-height content-empty)',
28
28
  );
29
29
 
30
30
  export const ScrollAreaHorizontalFades = withClassName(
31
31
  'div',
32
- 'layer-components:(pointer-events-none absolute inset-0 [--scroll-area-overflow-x-start:inherit] [--scroll-area-overflow-x-end:inherit])',
33
- 'layer-components:before:(content-empty [--scroll-area-overflow-x-start:inherit] left-0 block top-0 h-full absolute transition-width w-[min(40px,var(--scroll-area-overflow-x-start,40px))] bg-gradient-to-r bg-gradient-from-bg bg-gradient-to-transparent)',
34
- 'layer-components:after:(content-empty [--scroll-area-overflow-x-end:inherit] right-0 block top-0 h-full absolute transition-width w-[min(40px,var(--scroll-area-overflow-x-end,40px))] bg-gradient-to-l bg-gradient-from-bg bg-gradient-to-transparent)',
32
+ 'layer-components:([--scroll-area-overflow-x-end:inherit] [--scroll-area-overflow-x-start:inherit] pointer-events-none absolute inset-0)',
33
+ 'layer-components:before:([--scroll-area-overflow-x-start:inherit] absolute left-0 top-0 block h-full w-[min(40px,var(--scroll-area-overflow-x-start,40px))] bg-gradient-from-bg bg-gradient-to-transparent bg-gradient-to-r transition-width content-empty)',
34
+ 'layer-components:after:([--scroll-area-overflow-x-end:inherit] absolute right-0 top-0 block h-full w-[min(40px,var(--scroll-area-overflow-x-end,40px))] bg-gradient-from-bg bg-gradient-to-transparent bg-gradient-to-l transition-width content-empty)',
35
35
  );
36
36
 
37
37
  export const ScrollAreaViewportFades = () => (
@@ -76,14 +76,14 @@ ComposedScrollbar.displayName = 'ScrollAreaScrollbar';
76
76
 
77
77
  export const ScrollAreaScrollbar = withClassName(
78
78
  ComposedScrollbar,
79
- 'layer-components:(flex rounded-full relative select-none touch-none pointer-events-none m-xxs opacity-0)',
80
- 'layer-components:(bg-fg/5 transition-[opacity,height,width])',
81
- 'layer-components:data-[hovering]:(opacity-100 pointer-events-auto)',
79
+ 'layer-components:(pointer-events-none relative m-xxs flex touch-none select-none rounded-full opacity-0)',
80
+ 'layer-components:(transition-[opacity,height,width] bg-fg/5)',
81
+ 'layer-components:data-[hovering]:(pointer-events-auto opacity-100)',
82
82
  'layer-components:data-[scrolling]:(duration-0)',
83
- 'layer-components:before:(content-empty absolute)',
83
+ 'layer-components:before:(absolute content-empty)',
84
84
 
85
- 'layer-components:data-[orientation=vertical]:(w-0.25rem justify-center before:(w-1.25rem h-full) hover:w-0.5rem)',
86
- 'layer-components:data-[orientation=horizontal]:(h-0.25rem items-center before:(h-1.25rem w-full) hover:h-0.5rem)',
85
+ 'layer-components:data-[orientation=vertical]:(w-0.25rem justify-center before:h-full before:w-1.25rem hover:w-0.5rem)',
86
+ 'layer-components:data-[orientation=horizontal]:(h-0.25rem items-center before:h-1.25rem before:w-full hover:h-0.5rem)',
87
87
  );
88
88
 
89
89
  export const ScrollAreaCorner = withClassName(
@@ -1,97 +1,103 @@
1
- import { Slider as BaseSlider, SliderRootProps } from '@base-ui/react/slider';
2
- import clsx from 'clsx';
3
- import { Ref } from 'react';
4
- import { withClassName } from '../../hooks.js';
5
- import { PaletteName } from '../../uno/index.js';
6
-
7
- export const SliderRoot = withClassName(
8
- BaseSlider.Root,
9
- 'layer-components:w-full',
10
- );
11
-
12
- export const SliderControl = withClassName(
13
- BaseSlider.Control,
14
- 'layer-components:(relative flex items-center select-none touch-none w-full h-30px translate-z-0)',
15
- 'layer-variants:[&[data-orientation=vertical]]:(flex-col h-full w-30px)',
16
- );
17
- export const SliderTrack = withClassName(
18
- BaseSlider.Track,
19
- 'layer-components:(bg-transparent relative grow rounded-lg h-7px ring-1 ring-black transition-colors select-none)',
20
- 'layer-variants:[&[data-orientation=vertical]]:(w-7px h-full flex-1)',
21
- );
22
- export const SliderRange = withClassName(
23
- BaseSlider.Indicator,
24
- 'layer-components:(bg-main rounded-lg transition-colors)',
25
- 'layer-variants:[&[data-orientation=vertical]]:(bg-main)',
26
- );
27
- export const SliderThumb = withClassName(
28
- BaseSlider.Thumb,
29
- 'layer-components:(flex items-center justify-center leading-none cursor-pointer w-5 h-5 bg-white shadow-sm rounded-lg ring-2 ring-black touch-none transition-color)',
30
- 'layer-components:hover:(shadow-md)',
31
- 'layer-components:active:(shadow-lg ring-4 ring-main-dark ring-opacity-50 bg-main-light)',
32
- 'layer-components:focus-visible:(shadow-lg ring-4 ring-accent ring-opacity-50 outline-none bg-main-light)',
33
- 'layer-components:focus:(outline-none)',
34
- 'layer-components:disabled:(opacity-50)',
35
- );
36
-
37
- export const SliderBase = ({
38
- children,
39
- color,
40
- className,
41
- ...props
42
- }: SliderProps) => {
43
- return (
44
- <SliderRoot
45
- {...props}
46
- className={clsx(color && `palette-${color}`, className)}
47
- >
48
- <SliderControl>{children}</SliderControl>
49
- </SliderRoot>
50
- );
51
- };
52
-
53
- /**
54
- * A pre-configured Slider UI component. Meant to compose
55
- * under Slider.Root when you want to use Slider.Value, too
56
- */
57
- export const SliderUi = ({
58
- label,
59
- className,
60
- }: {
61
- label?: string;
62
- className?: string;
63
- }) => (
64
- <Slider.Control className={className}>
65
- <SliderTrack>
66
- <SliderRange />
67
- <SliderThumb aria-label={label} />
68
- </SliderTrack>
69
- </Slider.Control>
70
- );
71
-
72
- export interface SliderProps extends SliderRootProps {
73
- color?: PaletteName;
74
- ref?: Ref<HTMLDivElement>;
75
- }
76
-
77
- export const Slider = Object.assign(
78
- function Slider(props: SliderProps) {
79
- return (
80
- <SliderRoot {...props}>
81
- <SliderUi />
82
- </SliderRoot>
83
- );
84
- },
85
- {
86
- Root: SliderRoot,
87
- Base: SliderBase,
88
- Control: SliderControl,
89
- Track: SliderTrack,
90
- /** @deprecated - use Indicator */
91
- Range: SliderRange,
92
- Indicator: SliderRange,
93
- Thumb: SliderThumb,
94
- Value: BaseSlider.Value,
95
- Ui: SliderUi,
96
- },
97
- );
1
+ import { Slider as BaseSlider, SliderRootProps } from '@base-ui/react/slider';
2
+ import clsx from 'clsx';
3
+ import { Ref } from 'react';
4
+ import { withClassName } from '../../hooks.js';
5
+ import { PaletteName } from '../../uno/index.js';
6
+
7
+ export const SliderRoot = withClassName(
8
+ BaseSlider.Root,
9
+ 'layer-components:w-full',
10
+ );
11
+
12
+ export const SliderControl = withClassName(
13
+ BaseSlider.Control,
14
+ 'layer-components:(relative h-30px w-full flex translate-z-0 touch-none select-none items-center)',
15
+ 'layer-variants:[&[data-orientation=vertical]]:(h-full w-30px flex-col)',
16
+ );
17
+ export const SliderTrack = withClassName(
18
+ BaseSlider.Track,
19
+ 'layer-components:(relative h-7px grow select-none rounded-lg ring-1 transition-colors bg-transparent ring-black)',
20
+ 'layer-variants:[&[data-orientation=vertical]]:(h-full w-7px flex-1)',
21
+ );
22
+ export const SliderRange = withClassName(
23
+ BaseSlider.Indicator,
24
+ 'layer-components:(rounded-lg transition-colors bg-main)',
25
+ 'layer-variants:[&[data-orientation=vertical]]:(bg-main)',
26
+ );
27
+ export const SliderThumb = withClassName(
28
+ BaseSlider.Thumb,
29
+ 'layer-components:(h-5 w-5 flex cursor-pointer touch-none items-center justify-center rounded-lg leading-none shadow-sm ring-2 transition-color bg-white ring-black)',
30
+ 'layer-components:hover:(shadow-md)',
31
+ 'layer-components:active:(shadow-lg ring-4 ring-opacity-50 bg-main-light ring-main-dark)',
32
+ 'layer-components:focus-visible:(shadow-lg bg-main-light)',
33
+ 'foc-lg',
34
+ 'layer-components:focus:(outline-none)',
35
+ 'layer-components:disabled:(opacity-50)',
36
+ );
37
+
38
+ export const SliderBase = ({
39
+ children,
40
+ color,
41
+ className,
42
+ ...props
43
+ }: SliderProps) => {
44
+ return (
45
+ <SliderRoot
46
+ {...props}
47
+ className={clsx(color && `palette-${color}`, className)}
48
+ >
49
+ <SliderControl>{children}</SliderControl>
50
+ </SliderRoot>
51
+ );
52
+ };
53
+
54
+ /**
55
+ * A pre-configured Slider UI component. Meant to compose
56
+ * under Slider.Root when you want to use Slider.Value, too
57
+ */
58
+ export const SliderUi = ({
59
+ label,
60
+ className,
61
+ }: {
62
+ label?: string;
63
+ className?: string;
64
+ }) => (
65
+ <Slider.Control className={className}>
66
+ <SliderTrack>
67
+ <SliderRange />
68
+ <SliderThumb aria-label={label} />
69
+ </SliderTrack>
70
+ </Slider.Control>
71
+ );
72
+
73
+ export interface SliderProps extends SliderRootProps {
74
+ color?: PaletteName;
75
+ ref?: Ref<HTMLDivElement>;
76
+ }
77
+
78
+ export const Slider = Object.assign(
79
+ function Slider(props: SliderProps) {
80
+ return (
81
+ <SliderRoot {...props}>
82
+ <SliderUi />
83
+ </SliderRoot>
84
+ );
85
+ },
86
+ {
87
+ Root: SliderRoot,
88
+ Base: SliderBase,
89
+ Control: SliderControl,
90
+ Track: SliderTrack,
91
+ /** @deprecated - use Indicator */
92
+ Range: SliderRange,
93
+ Indicator: SliderRange,
94
+ Thumb: SliderThumb,
95
+ Value: BaseSlider.Value,
96
+ Ui: SliderUi,
97
+
98
+ getSingle: (v: number | readonly number[]) => {
99
+ if (Array.isArray(v)) return v[0];
100
+ return v;
101
+ },
102
+ },
103
+ );
@@ -1,38 +1,38 @@
1
- import { Switch as BaseSwitch, SwitchRootProps } from '@base-ui/react/switch';
2
- import { withClassName } from '../../hooks/withClassName.js';
3
-
4
- const SwitchRoot = withClassName(
5
- BaseSwitch.Root,
6
- 'layer-components:(unset appearance-none flex transition-all cursor-pointer w-42px h-25px p-3px bg-gray-light rounded-lg relative border-default flex-shrink-0 shadow-sm shadow-inset)',
7
- 'layer-components:data-[checked]:bg-main',
8
- 'layer-components:hover:[&:not(:disabled)]:(bg-lighten-2 ring-2 ring-bg)',
9
- 'layer-components:active:[&:not(:disabled)]:(bg-darken-1 ring-4)',
10
- 'layer-components:focus:outline-none',
11
- 'layer-components:focus-visible:(ring-4 ring-accent outline-off)',
12
- );
13
-
14
- const SwitchThumb = withClassName(
15
- BaseSwitch.Thumb,
16
- 'layer-components:(block aspect-1 h-full bg-white rounded-lg border-default transition-transform will-change-transform shadow-sm)',
17
- 'layer-components:data-[checked]:(translate-x-1rem)',
18
- 'layer-components:data-[checked]:before:(content-["✓"] flex items-center justify-center absolute top-0 left-0 w-full h-full text-xs text-main-ink)',
19
- );
20
-
21
- export const Switch = Object.assign(
22
- function Switch({
23
- ref,
24
- ...props
25
- }: SwitchRootProps & {
26
- ref?: React.Ref<HTMLButtonElement>;
27
- }) {
28
- return (
29
- <SwitchRoot {...props} ref={ref}>
30
- <SwitchThumb />
31
- </SwitchRoot>
32
- );
33
- },
34
- {
35
- Root: SwitchRoot,
36
- Thumb: SwitchThumb,
37
- },
38
- );
1
+ import { Switch as BaseSwitch, SwitchRootProps } from '@base-ui/react/switch';
2
+ import { withClassName } from '../../hooks/withClassName.js';
3
+
4
+ const SwitchRoot = withClassName(
5
+ BaseSwitch.Root,
6
+ 'layer-components:(unset relative h-25px w-42px flex flex-shrink-0 cursor-pointer appearance-none border-default rounded-lg p-3px shadow-sm shadow-inset transition-all bg-gray-light)',
7
+ 'layer-components:data-[checked]:bg-main',
8
+ 'layer-components:hover:[&:not(:disabled)]:(ring-2 bg-lighten-2 ring-bg)',
9
+ 'layer-components:active:[&:not(:disabled)]:(ring-4 bg-darken-1)',
10
+ 'layer-components:focus:outline-none',
11
+ 'layer-components:foc',
12
+ );
13
+
14
+ const SwitchThumb = withClassName(
15
+ BaseSwitch.Thumb,
16
+ 'layer-components:(block aspect-1 h-full border-default rounded-lg shadow-sm transition-transform will-change-transform bg-white)',
17
+ 'layer-components:data-[checked]:(translate-x-1rem)',
18
+ 'layer-components:data-[checked]:before:(absolute left-0 top-0 h-full w-full flex items-center justify-center text-xs text-main-ink content-["✓"])',
19
+ );
20
+
21
+ export const Switch = Object.assign(
22
+ function Switch({
23
+ ref,
24
+ ...props
25
+ }: SwitchRootProps & {
26
+ ref?: React.Ref<HTMLButtonElement>;
27
+ }) {
28
+ return (
29
+ <SwitchRoot {...props} ref={ref}>
30
+ <SwitchThumb />
31
+ </SwitchRoot>
32
+ );
33
+ },
34
+ {
35
+ Root: SwitchRoot,
36
+ Thumb: SwitchThumb,
37
+ },
38
+ );
@@ -24,7 +24,8 @@ export const TabsTrigger = withClassName(
24
24
  'layer-components:(min-h-touch min-w-100px flex-shrink-0)',
25
25
  'layer-components:(border rounded-lg bg-transparent border-transparent)',
26
26
  'layer-components:hover:[&[data-state=inactive]]:(ring-4 bg-gray-light bg-darken-1 ring-bg)',
27
- 'layer-components:focus-visible:(outline-off border ring-4 bg-darken-1 border-black ring-accent)',
27
+ 'layer-components:focus-visible:(border bg-darken-1 border-black)',
28
+ 'foc',
28
29
  'data-[active]:(cursor-default)',
29
30
  );
30
31
 
@@ -14,7 +14,7 @@ export const ToggleGroupItem = withClassName(
14
14
  'layer-components:(flex cursor-pointer items-center justify-center border-1 rounded-lg border-solid px-md py-sm text-nowrap transition-all color-black bg-gray/30 border-transparent)',
15
15
  'layer-components:hover:(ring-2 bg-lighten-2 border-main-ink/20 ring-bg)',
16
16
  'layer-components:active:(ring-4 bg-darken-1 border-main-ink/30)',
17
- 'layer-components:focus-visible:(outline-off ring-4 ring-accent)',
17
+ 'layer-components:foc',
18
18
  'layer-components:data-[pressed]:(border-default shadow-sm bg-main hover:border-default)',
19
19
  );
20
20
 
@@ -118,7 +118,7 @@ export function ViewportRoot({
118
118
  <div
119
119
  className={clsx(
120
120
  'contain-strict relative h-full w-full flex-1 touch-none select-none overflow-hidden',
121
- 'focus-visible:(ring ring-accent)',
121
+ 'foc',
122
122
  className,
123
123
  )}
124
124
  {...gestureProps}
@@ -181,14 +181,18 @@ function useReactToViewportChanges(
181
181
  }
182
182
 
183
183
  export interface VirtualKeyboardFocusOptions {
184
- focusElementTypes?: string[];
184
+ shouldScrollIntoView?: (el: Element) => boolean;
185
+ }
186
+ function defaultShouldScrollIntoView(el: Element) {
187
+ if (['input', 'textarea', 'select'].includes(el.tagName.toLowerCase()))
188
+ return true;
189
+ if (el.hasAttribute('contenteditable')) return true;
190
+ return false;
185
191
  }
186
192
  export function useVirtualKeyboardFocusBehavior({
187
- focusElementTypes = ['input', 'textarea', 'select'],
193
+ shouldScrollIntoView = defaultShouldScrollIntoView,
188
194
  }: VirtualKeyboardFocusOptions = {}) {
189
- const stableFocusElementTypes = focusElementTypes
190
- .map((type) => type.toLowerCase())
191
- .join(',');
195
+ const stableShouldScroll = useStableCallback(shouldScrollIntoView);
192
196
  useEffect(() => {
193
197
  if (typeof navigator === 'undefined' || typeof window === 'undefined') {
194
198
  return;
@@ -204,16 +208,20 @@ export function useVirtualKeyboardFocusBehavior({
204
208
 
205
209
  const virtualKeyboard = navigator.virtualKeyboard as any;
206
210
 
207
- const matchElements = stableFocusElementTypes.split(',');
208
-
209
211
  function update() {
210
- // const open = virtualKeyboard.boundingRect.height > 0;
212
+ const open = virtualKeyboard.boundingRect.height > 0;
213
+ if (open) {
214
+ console.log('keyboard opened');
215
+ }
211
216
  const activeElement = document.activeElement;
212
- if (
213
- activeElement &&
214
- matchElements.includes(activeElement.tagName.toLowerCase())
215
- ) {
216
- activeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
217
+ if (activeElement && stableShouldScroll(activeElement)) {
218
+ setTimeout(() => {
219
+ console.log('scroll focused element');
220
+ activeElement.scrollIntoView({
221
+ behavior: 'instant',
222
+ block: 'start',
223
+ });
224
+ }, 10);
217
225
  }
218
226
  }
219
227
 
@@ -221,5 +229,5 @@ export function useVirtualKeyboardFocusBehavior({
221
229
  return () => {
222
230
  virtualKeyboard.removeEventListener('geometrychange', update);
223
231
  };
224
- }, [stableFocusElementTypes]);
232
+ }, [stableShouldScroll]);
225
233
  }
@@ -1,5 +1,11 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
- import { Input, TextArea } from './components/index.js';
2
+ import {
3
+ Input,
4
+ PageContent,
5
+ PageNowPlaying,
6
+ PageRoot,
7
+ TextArea,
8
+ } from './components/index.js';
3
9
  import { useVirtualKeyboardBehavior } from './hooks/useVirtualKeyboardBehavior.js';
4
10
 
5
11
  const meta = {
@@ -19,16 +25,21 @@ export const Default: Story = {
19
25
  render(args) {
20
26
  useVirtualKeyboardBehavior('overlay');
21
27
  return (
22
- <>
23
- <div className="h-screen flex flex-col">
24
- <div className="flex flex-grow flex-col items-center justify-center p-lg">
25
- Focus the inputs below to see how the virtual keyboard behavior
26
- works.
28
+ <PageRoot id="root">
29
+ <PageContent>
30
+ <div className="h-screen flex flex-col">
31
+ <div className="flex flex-grow flex-col items-center justify-center p-lg">
32
+ Focus the inputs below to see how the virtual keyboard behavior
33
+ works.
34
+ </div>
35
+ <Input className="w-full" />
36
+ <TextArea className="w-full" />
27
37
  </div>
28
- <Input className="w-full" />
29
- <TextArea className="w-full" />
30
- </div>
31
- </>
38
+ <PageNowPlaying keepAboveKeyboard>
39
+ <Input placeholder="now playing" />
40
+ </PageNowPlaying>
41
+ </PageContent>
42
+ </PageRoot>
32
43
  );
33
44
  },
34
45
  };
@@ -35,6 +35,7 @@ import {
35
35
  Progress,
36
36
  Provider,
37
37
  Select,
38
+ Slider,
38
39
  TextSkeleton,
39
40
  ToggleGroup,
40
41
  Tooltip,
@@ -242,6 +243,7 @@ export function DemoUI({ className }: { className?: string }) {
242
243
  onChange={() => {}}
243
244
  />
244
245
  <Progress value={50} className="m-auto" />
246
+ <Slider defaultValue={50} min={0} max={100} />
245
247
  <Box surface color="primary" p gap d="col">
246
248
  <H1>Primary surface</H1>
247
249
  <H2>Primary surface</H2>
@@ -17,6 +17,7 @@ export const PROPS = {
17
17
  SERIF: '--u-font-serif',
18
18
  TITLE: '--u-font-title',
19
19
  },
20
+ FOCUS_COLOR: '--u-focus-color',
20
21
  },
21
22
 
22
23
  PALETTE: {
@@ -134,5 +135,10 @@ export const PROPS = {
134
135
  SHADOW_COLOR: '--un-shadow-color',
135
136
  SHADOW_OPACITY: '--un-shadow-opacity',
136
137
  SHADOW: '--un-shadow',
138
+ RING_WIDTH: '--un-ring-width',
139
+ RING_STYLE: '--un-ring-style',
140
+ RING_OFFSET_SHADOW: '--un-ring-offset-shadow',
141
+ RING_SHADOW: '--un-ring-shadow',
142
+ RING_INSET: '--un-ring-inset',
137
143
  },
138
144
  };
@@ -3,7 +3,7 @@ import { basePreflight } from './base.js';
3
3
  import { colorPreflight, ColorPreflightOptions } from './colors.js';
4
4
  import { fontsPreflight, FontsPreflightOptions } from './fonts.js';
5
5
  import { globalPreflight, GlobalsPreflightConfig } from './globals.js';
6
- import { keyboardPreflight } from './keyboard.js';
6
+ import { keyboardPreflight, KeyboardPreflightOptions } from './keyboard.js';
7
7
  import { layerPreflight } from './layers.js';
8
8
  import { modePreflight } from './mode.js';
9
9
  import { propertiesPreflight } from './properties.js';
@@ -12,7 +12,8 @@ import { userPreflight, UserPreflightOptions } from './user.js';
12
12
  export type PreflightConfig = FontsPreflightOptions &
13
13
  GlobalsPreflightConfig &
14
14
  UserPreflightOptions &
15
- ColorPreflightOptions;
15
+ ColorPreflightOptions &
16
+ KeyboardPreflightOptions;
16
17
 
17
18
  export const preflights = (config: PreflightConfig): Preflight<any>[] => [
18
19
  layerPreflight,
@@ -23,5 +24,5 @@ export const preflights = (config: PreflightConfig): Preflight<any>[] => [
23
24
  fontsPreflight(config),
24
25
  propertiesPreflight,
25
26
  userPreflight(config),
26
- keyboardPreflight,
27
+ keyboardPreflight(config),
27
28
  ];
@@ -1,12 +1,19 @@
1
1
  import { preflight } from './_util.js';
2
2
 
3
- export const keyboardPreflight = preflight({
4
- getCSS: () =>
5
- // add space to bottom of body equal to virtual keyboard inset so that
6
- // content is not hidden behind the keyboard
7
- `
8
- body {
9
- margin-bottom: env(virtual-keyboard-inset-bottom, 0px) !important;
3
+ export interface KeyboardPreflightOptions {
4
+ rootSelector?: string;
5
+ }
6
+
7
+ export const keyboardPreflight = ({
8
+ rootSelector = '#root,#main',
9
+ }: KeyboardPreflightOptions) =>
10
+ preflight({
11
+ getCSS: () =>
12
+ // add space to bottom of body equal to virtual keyboard inset so that
13
+ // content is not hidden behind the keyboard
14
+ `
15
+ ${rootSelector} {
16
+ transform: translateY(calc(-1 * env(keyboard-inset-height,0px)));
10
17
  }
11
18
  `,
12
- });
19
+ });
@@ -18,6 +18,7 @@ export interface UserPreflightOptions {
18
18
  cornerScale?: number;
19
19
  shadowSpread?: number;
20
20
  fontSize?: number;
21
+ focusColor?: 'black' | 'primary' | 'accent' | 'gray';
21
22
  }
22
23
 
23
24
  export const userPreflight = ({
@@ -30,6 +31,7 @@ export const userPreflight = ({
30
31
  shadowSpread = 1,
31
32
  namedHues,
32
33
  fontSize = 16,
34
+ focusColor = 'accent',
33
35
  }: UserPreflightOptions) =>
34
36
  preflight({
35
37
  getCSS: () => {
@@ -44,6 +46,7 @@ export const userPreflight = ({
44
46
 
45
47
  ${PROPS.USER.COLOR.PRIMARY_HUE}: ${primaryHue};
46
48
  ${PROPS.USER.COLOR.ACCENT_HUE}: ${accentHue};
49
+ ${PROPS.USER.FOCUS_COLOR}: var(${focusColors[focusColor]});
47
50
  ${
48
51
  namedHues
49
52
  ? Object.entries(namedHues)
@@ -65,3 +68,13 @@ export const userPreflight = ({
65
68
  `;
66
69
  },
67
70
  });
71
+
72
+ const focusColors: Record<
73
+ NonNullable<UserPreflightOptions['focusColor']>,
74
+ string
75
+ > = {
76
+ black: PROPS.MODE.BLACK,
77
+ primary: PROPS.PALETTE.NAMED_SHADES('primary').DARK,
78
+ accent: PROPS.PALETTE.NAMED_SHADES('accent').MID,
79
+ gray: PROPS.PALETTE.GRAY_SHADES.DARK,
80
+ };
@@ -0,0 +1,46 @@
1
+ import { Theme } from '@unocss/preset-mini';
2
+ import { Rule } from 'unocss';
3
+ import { PROPS } from '../logic/properties.js';
4
+
5
+ export const focusRules: Rule<Theme>[] = [
6
+ [
7
+ /^foc(?:-effect)?(-lg)?$/,
8
+ function* ([, size], { symbols }) {
9
+ yield {
10
+ [symbols.selector]: (selector) =>
11
+ `${selector}:focus-visible:not([data-focus-clicked]), ${selector}[data-focus-visible]`,
12
+ [PROPS.BUILT_IN.RING_COLOR]: `var(${PROPS.USER.FOCUS_COLOR})`,
13
+ [PROPS.BUILT_IN.RING_WIDTH]: size === '-lg' ? '4px' : '2px',
14
+ [PROPS.BUILT_IN.RING_STYLE]: 'solid',
15
+ [PROPS.BUILT_IN
16
+ .RING_SHADOW]: `var(${PROPS.BUILT_IN.RING_INSET}) 0 0 0 var(${PROPS.BUILT_IN.RING_WIDTH}) var(${PROPS.BUILT_IN.RING_COLOR})`,
17
+ outline: 'none',
18
+ 'box-shadow': `var(${PROPS.BUILT_IN.RING_OFFSET_SHADOW}), var(${PROPS.BUILT_IN.RING_SHADOW}), var(${PROPS.BUILT_IN.SHADOW})`,
19
+ };
20
+ yield {
21
+ [symbols.selector]: (selector) => `${selector}:focus`,
22
+ outline: 'none',
23
+ };
24
+ },
25
+ ],
26
+ [
27
+ /^foc-contained(-lg)?$/,
28
+ function* ([, size], { symbols }) {
29
+ yield {
30
+ [symbols.selector]: (selector) =>
31
+ `${selector}:has(:focus-visible:not([data-focus-clicked])), ${selector}:has([data-focus-visible])`,
32
+ [PROPS.BUILT_IN.RING_COLOR]: `var(${PROPS.USER.FOCUS_COLOR})`,
33
+ [PROPS.BUILT_IN.RING_WIDTH]: size === '-lg' ? '4px' : '2px',
34
+ [PROPS.BUILT_IN.RING_STYLE]: 'solid',
35
+ [PROPS.BUILT_IN
36
+ .RING_SHADOW]: `0 0 0 var(${PROPS.BUILT_IN.RING_WIDTH}) var(${PROPS.BUILT_IN.RING_COLOR})`,
37
+ outline: 'none',
38
+ 'box-shadow': `var(${PROPS.BUILT_IN.RING_OFFSET_SHADOW}), var(${PROPS.BUILT_IN.RING_SHADOW})`,
39
+ };
40
+ yield {
41
+ [symbols.selector]: (selector) => `${selector}>*:focus`,
42
+ outline: 'none',
43
+ };
44
+ },
45
+ ],
46
+ ];