@octanejs/radix 0.1.2

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -0
  3. package/package.json +45 -0
  4. package/src/AccessibleIcon.ts +26 -0
  5. package/src/Accordion.ts +282 -0
  6. package/src/AlertDialog.ts +110 -0
  7. package/src/Arrow.ts +21 -0
  8. package/src/AspectRatio.ts +34 -0
  9. package/src/Avatar.ts +152 -0
  10. package/src/Checkbox.ts +325 -0
  11. package/src/Collapsible.ts +170 -0
  12. package/src/ContextMenu.ts +303 -0
  13. package/src/Dialog.ts +308 -0
  14. package/src/DismissableLayer.ts +413 -0
  15. package/src/DropdownMenu.ts +275 -0
  16. package/src/FocusScope.ts +266 -0
  17. package/src/Form.ts +621 -0
  18. package/src/HoverCard.ts +318 -0
  19. package/src/Label.ts +25 -0
  20. package/src/Menu.ts +1057 -0
  21. package/src/Menubar.ts +572 -0
  22. package/src/NavigationMenu.ts +1236 -0
  23. package/src/OneTimePasswordField.ts +977 -0
  24. package/src/PasswordToggleField.ts +495 -0
  25. package/src/Popover.ts +354 -0
  26. package/src/Popper.ts +401 -0
  27. package/src/Portal.ts +19 -0
  28. package/src/Presence.ts +225 -0
  29. package/src/Primitive.ts +53 -0
  30. package/src/Progress.ts +92 -0
  31. package/src/Radio.ts +262 -0
  32. package/src/RadioGroup.ts +212 -0
  33. package/src/RovingFocusGroup.ts +259 -0
  34. package/src/ScrollArea.ts +966 -0
  35. package/src/Select.ts +1901 -0
  36. package/src/Separator.ts +36 -0
  37. package/src/Slider.ts +745 -0
  38. package/src/Slot.ts +105 -0
  39. package/src/Switch.ts +278 -0
  40. package/src/Tabs.ts +177 -0
  41. package/src/Toast.ts +923 -0
  42. package/src/Toggle.ts +31 -0
  43. package/src/ToggleGroup.ts +157 -0
  44. package/src/Toolbar.ts +130 -0
  45. package/src/Tooltip.ts +669 -0
  46. package/src/VisuallyHidden.ts +29 -0
  47. package/src/collection.ts +97 -0
  48. package/src/compose-event-handlers.ts +15 -0
  49. package/src/compose-refs.ts +53 -0
  50. package/src/context.ts +109 -0
  51. package/src/direction.ts +19 -0
  52. package/src/focus-guards.ts +56 -0
  53. package/src/index.ts +54 -0
  54. package/src/internal.ts +42 -0
  55. package/src/scroll-lock.ts +57 -0
  56. package/src/use-callback-ref.ts +28 -0
  57. package/src/use-effect-event.ts +36 -0
  58. package/src/use-is-hydrated.ts +23 -0
  59. package/src/use-previous.ts +28 -0
  60. package/src/use-size.ts +57 -0
  61. package/src/useControllableState.ts +78 -0
  62. package/src/useId.ts +15 -0
package/src/Toggle.ts ADDED
@@ -0,0 +1,31 @@
1
+ // Ported from @radix-ui/react-toggle. A two-state button: `aria-pressed` +
2
+ // `data-state="on"/"off"` over a controllable `pressed`.
3
+ import { createElement } from 'octane';
4
+
5
+ import { composeEventHandlers } from './compose-event-handlers';
6
+ import { S, subSlot } from './internal';
7
+ import { Primitive } from './Primitive';
8
+ import { useControllableState } from './useControllableState';
9
+
10
+ export function Toggle(props: any): any {
11
+ const slot = S('Toggle');
12
+ const { pressed: pressedProp, defaultPressed, onPressedChange, ...buttonProps } = props ?? {};
13
+ const [pressed, setPressed] = useControllableState<boolean>(
14
+ { prop: pressedProp, onChange: onPressedChange, defaultProp: defaultPressed ?? false },
15
+ subSlot(slot, 'pressed'),
16
+ );
17
+ return createElement(Primitive.button, {
18
+ type: 'button',
19
+ 'aria-pressed': pressed,
20
+ 'data-state': pressed ? 'on' : 'off',
21
+ 'data-disabled': props?.disabled ? '' : undefined,
22
+ ...buttonProps,
23
+ onClick: composeEventHandlers(props?.onClick, () => {
24
+ if (!props?.disabled) {
25
+ setPressed(!pressed);
26
+ }
27
+ }),
28
+ });
29
+ }
30
+
31
+ export { Toggle as Root };
@@ -0,0 +1,157 @@
1
+ // Ported from @radix-ui/react-toggle-group. A set of Toggles where `type="single"` keeps
2
+ // at most one pressed (role=radiogroup semantics per item) and `type="multiple"` allows
3
+ // any combination. Items participate in a RovingFocusGroup (single tab stop + arrow-key
4
+ // navigation) unless `rovingFocus={false}`.
5
+ import { createElement, useCallback, useMemo } from 'octane';
6
+
7
+ import { createContextScope } from './context';
8
+ import { S, subSlot } from './internal';
9
+ import { Primitive } from './Primitive';
10
+ import * as RovingFocusGroup from './RovingFocusGroup';
11
+ import { createRovingFocusGroupScope } from './RovingFocusGroup';
12
+ import { Toggle } from './Toggle';
13
+ import { useControllableState } from './useControllableState';
14
+
15
+ const [createToggleGroupContext, createToggleGroupScope] = createContextScope('ToggleGroup', [
16
+ createRovingFocusGroupScope,
17
+ ]);
18
+ export { createToggleGroupScope };
19
+ const useRovingFocusGroupScope = createRovingFocusGroupScope();
20
+
21
+ interface ValueContext {
22
+ type: 'single' | 'multiple';
23
+ value: string[];
24
+ onItemActivate: (value: string) => void;
25
+ onItemDeactivate: (value: string) => void;
26
+ }
27
+ const [ToggleGroupValueProvider, useToggleGroupValueContext] =
28
+ createToggleGroupContext<ValueContext>('ToggleGroup');
29
+ const [ToggleGroupContext, useToggleGroupContext] = createToggleGroupContext<{
30
+ rovingFocus: boolean;
31
+ disabled: boolean;
32
+ }>('ToggleGroup');
33
+
34
+ export function Root(props: any): any {
35
+ const { type, ...toggleGroupProps } = props ?? {};
36
+ if (type === 'single') {
37
+ return createElement(ImplSingle, { role: 'radiogroup', ...toggleGroupProps });
38
+ }
39
+ if (type === 'multiple') {
40
+ return createElement(ImplMultiple, { role: 'toolbar', ...toggleGroupProps });
41
+ }
42
+ throw new Error('Missing prop `type` expected on `ToggleGroup`');
43
+ }
44
+
45
+ function ImplSingle(props: any): any {
46
+ const slot = S('ToggleGroup.Single');
47
+ const { value: valueProp, defaultValue, onValueChange = () => {}, ...rest } = props;
48
+ const [value, setValue] = useControllableState<string>(
49
+ { prop: valueProp, defaultProp: defaultValue ?? '', onChange: onValueChange },
50
+ subSlot(slot, 'value'),
51
+ );
52
+ return createElement(ToggleGroupValueProvider, {
53
+ scope: props.__scopeToggleGroup,
54
+ type: 'single',
55
+ value: useMemo(() => (value ? [value] : []), [value], subSlot(slot, 'arr')),
56
+ onItemActivate: setValue,
57
+ onItemDeactivate: useCallback(() => setValue(''), [setValue], subSlot(slot, 'off')),
58
+ children: createElement(Impl, rest),
59
+ });
60
+ }
61
+
62
+ function ImplMultiple(props: any): any {
63
+ const slot = S('ToggleGroup.Multiple');
64
+ const { value: valueProp, defaultValue, onValueChange = () => {}, ...rest } = props;
65
+ const [value, setValue] = useControllableState<string[]>(
66
+ { prop: valueProp, defaultProp: defaultValue ?? [], onChange: onValueChange },
67
+ subSlot(slot, 'value'),
68
+ );
69
+ const handleButtonActivate = useCallback(
70
+ (itemValue: string) => setValue((prev: string[] = []) => [...prev, itemValue]),
71
+ [setValue],
72
+ subSlot(slot, 'on'),
73
+ );
74
+ const handleButtonDeactivate = useCallback(
75
+ (itemValue: string) => setValue((prev: string[] = []) => prev.filter((v) => v !== itemValue)),
76
+ [setValue],
77
+ subSlot(slot, 'off'),
78
+ );
79
+ return createElement(ToggleGroupValueProvider, {
80
+ scope: props.__scopeToggleGroup,
81
+ type: 'multiple',
82
+ value,
83
+ onItemActivate: handleButtonActivate,
84
+ onItemDeactivate: handleButtonDeactivate,
85
+ children: createElement(Impl, rest),
86
+ });
87
+ }
88
+
89
+ function Impl(props: any): any {
90
+ const slot = S('ToggleGroup.Impl');
91
+ const {
92
+ __scopeToggleGroup,
93
+ disabled = false,
94
+ rovingFocus = true,
95
+ orientation,
96
+ dir,
97
+ loop = true,
98
+ ...toggleGroupProps
99
+ } = props;
100
+ const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeToggleGroup, subSlot(slot, 'rfs'));
101
+ const direction = dir === 'rtl' ? 'rtl' : 'ltr';
102
+ const commonProps = { dir: direction, ...toggleGroupProps };
103
+ return createElement(ToggleGroupContext, {
104
+ scope: __scopeToggleGroup,
105
+ rovingFocus,
106
+ disabled,
107
+ children: rovingFocus
108
+ ? createElement(RovingFocusGroup.Root, {
109
+ asChild: true,
110
+ ...rovingFocusGroupScope,
111
+ orientation,
112
+ dir: direction,
113
+ loop,
114
+ children: createElement(Primitive.div, commonProps),
115
+ })
116
+ : createElement(Primitive.div, commonProps),
117
+ });
118
+ }
119
+
120
+ export function Item(props: any): any {
121
+ const slot = S('ToggleGroup.Item');
122
+ const valueContext = useToggleGroupValueContext('ToggleGroupItem', props?.__scopeToggleGroup);
123
+ const context = useToggleGroupContext('ToggleGroupItem', props?.__scopeToggleGroup);
124
+ const rovingFocusGroupScope = useRovingFocusGroupScope(
125
+ props?.__scopeToggleGroup,
126
+ subSlot(slot, 'rfs'),
127
+ );
128
+ const pressed = valueContext.value.includes(props.value);
129
+ const disabled = context.disabled || props.disabled;
130
+ const commonProps = { ...props, pressed, disabled };
131
+ return context.rovingFocus
132
+ ? createElement(RovingFocusGroup.Item, {
133
+ asChild: true,
134
+ ...rovingFocusGroupScope,
135
+ focusable: !disabled,
136
+ active: pressed,
137
+ children: createElement(ItemImpl, commonProps),
138
+ })
139
+ : createElement(ItemImpl, commonProps);
140
+ }
141
+
142
+ function ItemImpl(props: any): any {
143
+ const { __scopeToggleGroup, value, ...itemProps } = props;
144
+ const valueContext = useToggleGroupValueContext('ToggleGroupItem', __scopeToggleGroup);
145
+ const singleProps = { role: 'radio', 'aria-checked': props.pressed, 'aria-pressed': undefined };
146
+ const typeProps = valueContext.type === 'single' ? singleProps : undefined;
147
+ return createElement(Toggle, {
148
+ ...typeProps,
149
+ ...itemProps,
150
+ onPressedChange: (pressed: boolean) => {
151
+ if (pressed) valueContext.onItemActivate(value);
152
+ else valueContext.onItemDeactivate(value);
153
+ },
154
+ });
155
+ }
156
+
157
+ export { Root as ToggleGroup, Item as ToggleGroupItem };
package/src/Toolbar.ts ADDED
@@ -0,0 +1,130 @@
1
+ // Ported from @radix-ui/react-toolbar (source:
2
+ // .radix-primitives/packages/react/toolbar/src/toolbar.tsx). A `role=toolbar` container
3
+ // whose Buttons/Links/ToggleItems participate in one RovingFocusGroup; embeds ToggleGroup
4
+ // (with its own roving disabled — the toolbar owns focus) and Separator (flipped
5
+ // orientation).
6
+ import { createElement } from 'octane';
7
+
8
+ import { composeEventHandlers } from './compose-event-handlers';
9
+ import { createContextScope } from './context';
10
+ import { S, subSlot } from './internal';
11
+ import { Primitive } from './Primitive';
12
+ import * as RovingFocusGroup from './RovingFocusGroup';
13
+ import { createRovingFocusGroupScope } from './RovingFocusGroup';
14
+ import * as SeparatorPrimitive from './Separator';
15
+ import * as ToggleGroupPrimitive from './ToggleGroup';
16
+ import { createToggleGroupScope } from './ToggleGroup';
17
+
18
+ const [createToolbarContext, createToolbarScope] = createContextScope('Toolbar', [
19
+ createRovingFocusGroupScope,
20
+ createToggleGroupScope,
21
+ ]);
22
+ export { createToolbarScope };
23
+ const useRovingFocusGroupScope = createRovingFocusGroupScope();
24
+ const useToggleGroupScope = createToggleGroupScope();
25
+
26
+ const [ToolbarProvider, useToolbarContext] = createToolbarContext<{
27
+ orientation: 'horizontal' | 'vertical';
28
+ dir: 'ltr' | 'rtl';
29
+ }>('Toolbar');
30
+
31
+ export function Root(props: any): any {
32
+ const slot = S('Toolbar.Root');
33
+ const {
34
+ __scopeToolbar,
35
+ orientation = 'horizontal',
36
+ dir,
37
+ loop = true,
38
+ ...toolbarProps
39
+ } = props ?? {};
40
+ const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeToolbar, subSlot(slot, 'rfs'));
41
+ const direction = dir === 'rtl' ? 'rtl' : 'ltr';
42
+ return createElement(ToolbarProvider, {
43
+ scope: __scopeToolbar,
44
+ orientation,
45
+ dir: direction,
46
+ children: createElement(RovingFocusGroup.Root, {
47
+ asChild: true,
48
+ ...rovingFocusGroupScope,
49
+ orientation,
50
+ dir: direction,
51
+ loop,
52
+ children: createElement(Primitive.div, {
53
+ role: 'toolbar',
54
+ 'aria-orientation': orientation,
55
+ dir: direction,
56
+ ...toolbarProps,
57
+ }),
58
+ }),
59
+ });
60
+ }
61
+
62
+ export function Separator(props: any): any {
63
+ const { __scopeToolbar, ...separatorProps } = props ?? {};
64
+ const context = useToolbarContext('ToolbarSeparator', __scopeToolbar);
65
+ return createElement(SeparatorPrimitive.Root, {
66
+ orientation: context.orientation === 'horizontal' ? 'vertical' : 'horizontal',
67
+ ...separatorProps,
68
+ });
69
+ }
70
+
71
+ export function Button(props: any): any {
72
+ const slot = S('Toolbar.Button');
73
+ const { __scopeToolbar, ...buttonProps } = props ?? {};
74
+ const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeToolbar, subSlot(slot, 'rfs'));
75
+ return createElement(RovingFocusGroup.Item, {
76
+ asChild: true,
77
+ ...rovingFocusGroupScope,
78
+ focusable: !props?.disabled,
79
+ children: createElement(Primitive.button, { type: 'button', ...buttonProps }),
80
+ });
81
+ }
82
+
83
+ export function Link(props: any): any {
84
+ const slot = S('Toolbar.Link');
85
+ const { __scopeToolbar, ...linkProps } = props ?? {};
86
+ const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeToolbar, subSlot(slot, 'rfs'));
87
+ return createElement(RovingFocusGroup.Item, {
88
+ asChild: true,
89
+ ...rovingFocusGroupScope,
90
+ focusable: true,
91
+ children: createElement(Primitive.a, {
92
+ ...linkProps,
93
+ onKeyDown: composeEventHandlers(props?.onKeyDown, (event: KeyboardEvent) => {
94
+ if (event.key === ' ') (event.currentTarget as HTMLElement).click();
95
+ }),
96
+ }),
97
+ });
98
+ }
99
+
100
+ export function ToggleGroup(props: any): any {
101
+ const slot = S('Toolbar.ToggleGroup');
102
+ const { __scopeToolbar, ...toggleGroupProps } = props ?? {};
103
+ const context = useToolbarContext('ToolbarToggleGroup', __scopeToolbar);
104
+ const toggleGroupScope = useToggleGroupScope(__scopeToolbar, subSlot(slot, 'tgs'));
105
+ return createElement(ToggleGroupPrimitive.Root, {
106
+ 'data-orientation': context.orientation,
107
+ dir: context.dir,
108
+ ...toggleGroupScope,
109
+ ...toggleGroupProps,
110
+ // The toolbar is the roving group; the embedded toggle group must not compete.
111
+ rovingFocus: false,
112
+ });
113
+ }
114
+
115
+ export function ToggleItem(props: any): any {
116
+ const slot = S('Toolbar.ToggleItem');
117
+ const { __scopeToolbar, ...toggleItemProps } = props ?? {};
118
+ const toggleGroupScope = useToggleGroupScope(__scopeToolbar, subSlot(slot, 'tgs'));
119
+ const scope = { __scopeToolbar };
120
+ return createElement(Button, {
121
+ asChild: true,
122
+ ...scope,
123
+ children: createElement(ToggleGroupPrimitive.Item, {
124
+ ...toggleGroupScope,
125
+ ...toggleItemProps,
126
+ }),
127
+ });
128
+ }
129
+
130
+ export { Root as Toolbar, Button as ToolbarButton, Link as ToolbarLink };