@ariakit/solid-components 0.1.0

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 (94) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/as/as.d.ts +30 -0
  3. package/dist/as/as.d.ts.map +1 -0
  4. package/dist/as/as.js +40 -0
  5. package/dist/as/as.js.map +1 -0
  6. package/dist/focus-trap/focus-trap-region.d.ts +42 -0
  7. package/dist/focus-trap/focus-trap-region.d.ts.map +1 -0
  8. package/dist/focus-trap/focus-trap-region.js +71 -0
  9. package/dist/focus-trap/focus-trap-region.js.map +1 -0
  10. package/dist/focus-trap/focus-trap.d.ts +31 -0
  11. package/dist/focus-trap/focus-trap.d.ts.map +1 -0
  12. package/dist/focus-trap/focus-trap.js +42 -0
  13. package/dist/focus-trap/focus-trap.js.map +1 -0
  14. package/dist/group/group-label-context.d.ts +7 -0
  15. package/dist/group/group-label-context.d.ts.map +1 -0
  16. package/dist/group/group-label-context.js +7 -0
  17. package/dist/group/group-label-context.js.map +1 -0
  18. package/dist/group/group-label.d.ts +37 -0
  19. package/dist/group/group-label.d.ts.map +1 -0
  20. package/dist/group/group-label.js +51 -0
  21. package/dist/group/group-label.js.map +1 -0
  22. package/dist/group/group.d.ts +32 -0
  23. package/dist/group/group.d.ts.map +1 -0
  24. package/dist/group/group.js +49 -0
  25. package/dist/group/group.js.map +1 -0
  26. package/dist/heading/heading-context.d.ts +8 -0
  27. package/dist/heading/heading-context.d.ts.map +1 -0
  28. package/dist/heading/heading-context.js +7 -0
  29. package/dist/heading/heading-context.js.map +1 -0
  30. package/dist/heading/heading-level.d.ts +32 -0
  31. package/dist/heading/heading-level.d.ts.map +1 -0
  32. package/dist/heading/heading-level.js +34 -0
  33. package/dist/heading/heading-level.js.map +1 -0
  34. package/dist/heading/heading.d.ts +42 -0
  35. package/dist/heading/heading.d.ts.map +1 -0
  36. package/dist/heading/heading.js +57 -0
  37. package/dist/heading/heading.js.map +1 -0
  38. package/dist/heading/utils.d.ts +5 -0
  39. package/dist/heading/utils.d.ts.map +1 -0
  40. package/dist/heading/utils.js +0 -0
  41. package/dist/role/role.d.ts +34 -0
  42. package/dist/role/role.d.ts.map +1 -0
  43. package/dist/role/role.js +67 -0
  44. package/dist/role/role.js.map +1 -0
  45. package/dist/separator/separator.d.ts +36 -0
  46. package/dist/separator/separator.d.ts.map +1 -0
  47. package/dist/separator/separator.js +36 -0
  48. package/dist/separator/separator.js.map +1 -0
  49. package/dist/visually-hidden/visually-hidden.d.ts +37 -0
  50. package/dist/visually-hidden/visually-hidden.d.ts.map +1 -0
  51. package/dist/visually-hidden/visually-hidden.js +48 -0
  52. package/dist/visually-hidden/visually-hidden.js.map +1 -0
  53. package/index.ts +1 -0
  54. package/license +21 -0
  55. package/package.json +113 -0
  56. package/readme.md +19 -0
  57. package/solid/as/as.jsx +38 -0
  58. package/solid/as/as.jsx.map +1 -0
  59. package/solid/focus-trap/focus-trap-region.jsx +65 -0
  60. package/solid/focus-trap/focus-trap-region.jsx.map +1 -0
  61. package/solid/focus-trap/focus-trap.jsx +42 -0
  62. package/solid/focus-trap/focus-trap.jsx.map +1 -0
  63. package/solid/group/group-label-context.jsx +7 -0
  64. package/solid/group/group-label-context.jsx.map +1 -0
  65. package/solid/group/group-label.jsx +51 -0
  66. package/solid/group/group-label.jsx.map +1 -0
  67. package/solid/group/group.jsx +43 -0
  68. package/solid/group/group.jsx.map +1 -0
  69. package/solid/heading/heading-context.jsx +7 -0
  70. package/solid/heading/heading-context.jsx.map +1 -0
  71. package/solid/heading/heading-level.jsx +30 -0
  72. package/solid/heading/heading-level.jsx.map +1 -0
  73. package/solid/heading/heading.jsx +57 -0
  74. package/solid/heading/heading.jsx.map +1 -0
  75. package/solid/heading/utils.jsx +0 -0
  76. package/solid/role/role.jsx +67 -0
  77. package/solid/role/role.jsx.map +1 -0
  78. package/solid/separator/separator.jsx +36 -0
  79. package/solid/separator/separator.jsx.map +1 -0
  80. package/solid/visually-hidden/visually-hidden.jsx +48 -0
  81. package/solid/visually-hidden/visually-hidden.jsx.map +1 -0
  82. package/src/as/as.tsx +62 -0
  83. package/src/focus-trap/focus-trap-region.tsx +108 -0
  84. package/src/focus-trap/focus-trap.tsx +61 -0
  85. package/src/group/group-label-context.tsx +4 -0
  86. package/src/group/group-label.tsx +76 -0
  87. package/src/group/group.tsx +68 -0
  88. package/src/heading/heading-context.tsx +5 -0
  89. package/src/heading/heading-level.tsx +43 -0
  90. package/src/heading/heading.tsx +87 -0
  91. package/src/heading/utils.ts +1 -0
  92. package/src/role/role.tsx +91 -0
  93. package/src/separator/separator.tsx +66 -0
  94. package/src/visually-hidden/visually-hidden.tsx +63 -0
package/src/as/as.tsx ADDED
@@ -0,0 +1,62 @@
1
+ import { mergeProps } from "@ariakit/solid-utils";
2
+ import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js";
3
+ import { Dynamic } from "solid-js/web";
4
+
5
+ type AsElements = {
6
+ [K in keyof JSX.IntrinsicElements]: Component<ComponentProps<K>>;
7
+ };
8
+
9
+ type AsComponent = <T extends Component<any>, P = ComponentProps<T>>(
10
+ props: AsProps<T, P>,
11
+ ) => JSX.Element;
12
+
13
+ const cache = new Map<string, Component<any>>();
14
+
15
+ /**
16
+ * Allows a component to be rendered as a different HTML element or Solid
17
+ * component. Must be passed to the `render` prop of a component that
18
+ * supports it.
19
+ *
20
+ * To render as an HTML element, use `<As.element />` (e.g. `<As.button />`).
21
+ *
22
+ * To render as a component, use `<As component={Component} />` (e.g. `<As
23
+ * component={MyButton} />`).
24
+ *
25
+ * Check out the [Composition](https://solid.ariakit.com/guide/composition)
26
+ * guide for more details.
27
+ * @example
28
+ * ```jsx
29
+ * <Role render={<As component={MyButton} variant="primary" />} />
30
+ * <Role render={<As.button type="button" />} />
31
+ * ```
32
+ */
33
+ export const As = new Proxy(
34
+ function As(props: any) {
35
+ return ((parentProps: unknown) => (
36
+ // TODO: replace with LazyDynamic
37
+ <Dynamic
38
+ {...mergeProps(parentProps, props)}
39
+ component={props.component}
40
+ />
41
+ )) as unknown as JSX.Element;
42
+ } as AsComponent & AsElements,
43
+ {
44
+ get: (_, key: keyof JSX.IntrinsicElements) => {
45
+ let component = cache.get(key);
46
+ if (!component) {
47
+ component = function AsElement(props: any): JSX.Element {
48
+ return ((parentProps: unknown) => (
49
+ // TODO: replace with LazyDynamic
50
+ <Dynamic {...mergeProps(parentProps, props)} component={key} />
51
+ )) as unknown as JSX.Element;
52
+ };
53
+ cache.set(key, component);
54
+ }
55
+ return component;
56
+ },
57
+ },
58
+ );
59
+
60
+ export type AsProps<T extends ValidComponent, P = ComponentProps<T>> = {
61
+ [K in keyof P]: P[K];
62
+ } & { component: T };
@@ -0,0 +1,108 @@
1
+ import {
2
+ createRef,
3
+ mergeProps,
4
+ createHook,
5
+ createInstance,
6
+ withOptions,
7
+ wrapInstance,
8
+ } from "@ariakit/solid-utils";
9
+ import type { Options, Props } from "@ariakit/solid-utils";
10
+ import { getAllTabbableIn } from "@ariakit/utils";
11
+ import type { ValidComponent } from "solid-js";
12
+ import { Show } from "solid-js";
13
+ import { FocusTrap } from "./focus-trap.tsx";
14
+
15
+ const TagName = "div" satisfies ValidComponent;
16
+ type TagName = typeof TagName;
17
+ type HTMLType = HTMLElementTagNameMap[TagName];
18
+
19
+ /**
20
+ * Returns props to create a `FocusTrapRegion` component.
21
+ * @see https://solid.ariakit.com/components/focus-trap-region
22
+ * @example
23
+ * ```jsx
24
+ * const props = useFocusTrapRegion();
25
+ * <Role {...props} />
26
+ * ```
27
+ */
28
+ export const useFocusTrapRegion = createHook<TagName, FocusTrapRegionOptions>(
29
+ withOptions({ enabled: false }, function useFocusTrapRegion(props, options) {
30
+ const ref = createRef<HTMLType>();
31
+
32
+ props = wrapInstance(props, (wrapperProps) => {
33
+ const renderFocusTrap = () => {
34
+ return (
35
+ <Show when={options.enabled}>
36
+ <FocusTrap
37
+ onFocus={(event) => {
38
+ // TODO: (react) opportunity to extract into @ariakit/components?
39
+ const container = ref.current;
40
+ if (!container) return;
41
+ const tabbables = getAllTabbableIn(container, true);
42
+ const first = tabbables[0];
43
+ const last = tabbables[tabbables.length - 1];
44
+ // Fallbacks to the container element
45
+ if (!tabbables.length) {
46
+ container.focus();
47
+ return;
48
+ }
49
+ if (event.relatedTarget === first) {
50
+ last?.focus();
51
+ } else {
52
+ first?.focus();
53
+ }
54
+ }}
55
+ />
56
+ </Show>
57
+ );
58
+ };
59
+ return (
60
+ <>
61
+ {renderFocusTrap()}
62
+ {wrapperProps.children}
63
+ {renderFocusTrap()}
64
+ </>
65
+ );
66
+ });
67
+
68
+ props = mergeProps({ ref: ref.set }, props);
69
+
70
+ return props;
71
+ }),
72
+ );
73
+
74
+ /**
75
+ * Renders a wrapper element that traps the focus inside it when the
76
+ * [`enabled`](https://solid.ariakit.com/reference/focus-trap-region#enabled)
77
+ * prop is `true`.
78
+ * @see https://solid.ariakit.com/components/focus-trap
79
+ * @example
80
+ * ```jsx
81
+ * <FocusTrapRegion>
82
+ * <Button>click me</Button>
83
+ * <Button>trap focus</Button>
84
+ * <Button disabled>disabled Button</Button>
85
+ * </FocusTrapRegion>
86
+ * ```
87
+ */
88
+ export const FocusTrapRegion = function FocusTrapRegion(
89
+ props: FocusTrapRegionProps,
90
+ ) {
91
+ const htmlProps = useFocusTrapRegion(props);
92
+ return createInstance(TagName, htmlProps);
93
+ };
94
+
95
+ export interface FocusTrapRegionOptions<
96
+ _T extends ValidComponent = TagName,
97
+ > extends Options {
98
+ /**
99
+ * If true, it will trap the focus in the region.
100
+ * @default false
101
+ */
102
+ enabled?: boolean;
103
+ }
104
+
105
+ export type FocusTrapRegionProps<T extends ValidComponent = TagName> = Props<
106
+ T,
107
+ FocusTrapRegionOptions<T>
108
+ >;
@@ -0,0 +1,61 @@
1
+ import { mergeProps, createHook, createInstance } from "@ariakit/solid-utils";
2
+ import type { Props } from "@ariakit/solid-utils";
3
+ import type { ValidComponent } from "solid-js";
4
+ import type { VisuallyHiddenOptions } from "../visually-hidden/visually-hidden.tsx";
5
+ import { useVisuallyHidden } from "../visually-hidden/visually-hidden.tsx";
6
+
7
+ const TagName = "span" satisfies ValidComponent;
8
+ type TagName = typeof TagName;
9
+
10
+ /**
11
+ * Returns props to create a `FocusTrap` component.
12
+ * @see https://solid.ariakit.com/components/focus-trap
13
+ * @example
14
+ * ```jsx
15
+ * const props = useFocusTrap();
16
+ * <Role {...props} />
17
+ * ```
18
+ */
19
+ export const useFocusTrap = createHook<TagName, FocusTrapOptions>(
20
+ function useFocusTrap(props) {
21
+ props = mergeProps(
22
+ {
23
+ "data-focus-trap": "",
24
+ tabIndex: 0,
25
+ "aria-hidden": true,
26
+ style: {
27
+ // Prevents unintended scroll jumps.
28
+ position: "fixed",
29
+ top: 0,
30
+ left: 0,
31
+ },
32
+ },
33
+ props,
34
+ );
35
+
36
+ props = useVisuallyHidden(props);
37
+
38
+ return props;
39
+ },
40
+ );
41
+
42
+ /**
43
+ * Renders a focus trap element.
44
+ * @see https://solid.ariakit.com/components/focus-trap
45
+ * @example
46
+ * ```jsx
47
+ * <FocusTrap onFocus={focusSomethingElse} />
48
+ * ```
49
+ */
50
+ export function FocusTrap(props: FocusTrapProps) {
51
+ const htmlProps = useFocusTrap(props);
52
+ return createInstance(TagName, htmlProps);
53
+ }
54
+
55
+ export type FocusTrapOptions<T extends ValidComponent = TagName> =
56
+ VisuallyHiddenOptions<T>;
57
+
58
+ export type FocusTrapProps<T extends ValidComponent = TagName> = Props<
59
+ T,
60
+ FocusTrapOptions<T>
61
+ >;
@@ -0,0 +1,4 @@
1
+ import type { Setter } from "solid-js";
2
+ import { createContext } from "solid-js";
3
+
4
+ export const GroupLabelContext = createContext<Setter<string | undefined>>();
@@ -0,0 +1,76 @@
1
+ import {
2
+ createId,
3
+ mergeProps,
4
+ stableAccessor,
5
+ createHook,
6
+ createInstance,
7
+ } from "@ariakit/solid-utils";
8
+ import type { Options, Props } from "@ariakit/solid-utils";
9
+ import type { ValidComponent } from "solid-js";
10
+ import { createEffect, onCleanup, useContext } from "solid-js";
11
+ import { GroupLabelContext } from "./group-label-context.tsx";
12
+
13
+ const TagName = "div" satisfies ValidComponent;
14
+ type TagName = typeof TagName;
15
+
16
+ /**
17
+ * Returns props to create a `GroupLabel` component. This hook must be used in a
18
+ * component that's wrapped with `Group` so the `aria-labelledby` prop is
19
+ * properly set on the group element.
20
+ * @see https://solid.ariakit.com/components/group
21
+ * @example
22
+ * ```jsx
23
+ * // This component must be wrapped with Group
24
+ * const props = useGroupLabel();
25
+ * <Role {...props}>Label</Role>
26
+ * ```
27
+ */
28
+ export const useGroupLabel = createHook<TagName, GroupLabelOptions>(
29
+ function useGroupLabel(props) {
30
+ const setLabelId = useContext(GroupLabelContext);
31
+ const id = createId(stableAccessor(props, (p) => p.id));
32
+
33
+ createEffect(() => {
34
+ setLabelId?.(id());
35
+ onCleanup(() => setLabelId?.(undefined));
36
+ });
37
+
38
+ props = mergeProps(
39
+ {
40
+ get id() {
41
+ return id();
42
+ },
43
+ "aria-hidden": true,
44
+ },
45
+ props,
46
+ );
47
+
48
+ return props;
49
+ },
50
+ );
51
+
52
+ /**
53
+ * Renders a label in a group. This component should be wrapped with a
54
+ * [`Group`](https://solid.ariakit.com/reference/group) so the `aria-labelledby`
55
+ * prop is correctly set on the group element.
56
+ * @see https://solid.ariakit.com/components/group
57
+ * @example
58
+ * ```jsx
59
+ * <Group>
60
+ * <GroupLabel>Label</GroupLabel>
61
+ * </Group>
62
+ * ```
63
+ */
64
+ export const GroupLabel = function GroupLabel(props: GroupLabelProps) {
65
+ const htmlProps = useGroupLabel(props);
66
+ return createInstance(TagName, htmlProps);
67
+ };
68
+
69
+ export interface GroupLabelOptions<
70
+ _T extends ValidComponent = TagName,
71
+ > extends Options {}
72
+
73
+ export type GroupLabelProps<T extends ValidComponent = TagName> = Props<
74
+ T,
75
+ GroupLabelOptions<T>
76
+ >;
@@ -0,0 +1,68 @@
1
+ import {
2
+ mergeProps,
3
+ createHook,
4
+ createInstance,
5
+ wrapInstance,
6
+ } from "@ariakit/solid-utils";
7
+ import type { Options, Props } from "@ariakit/solid-utils";
8
+ import type { ValidComponent } from "solid-js";
9
+ import { createSignal } from "solid-js";
10
+ import { As } from "../as/as.tsx";
11
+ import { GroupLabelContext } from "./group-label-context.tsx";
12
+
13
+ const TagName = "div" satisfies ValidComponent;
14
+ type TagName = typeof TagName;
15
+
16
+ /**
17
+ * Returns props to create a `Group` component.
18
+ * @see https://solid.ariakit.com/components/group
19
+ * @example
20
+ * ```jsx
21
+ * const props = useGroup();
22
+ * <Role {...props}>Group</Role>
23
+ * ```
24
+ */
25
+ export const useGroup = createHook<TagName, GroupOptions>(
26
+ function useGroup(props) {
27
+ const [labelId, setLabelId] = createSignal<string>();
28
+
29
+ props = wrapInstance(
30
+ props,
31
+ <As component={GroupLabelContext.Provider} value={setLabelId} />,
32
+ );
33
+
34
+ props = mergeProps(
35
+ {
36
+ role: "group" as const,
37
+ get "aria-labelledby"() {
38
+ return labelId();
39
+ },
40
+ },
41
+ props,
42
+ );
43
+
44
+ return props;
45
+ },
46
+ );
47
+
48
+ /**
49
+ * Renders a group element. Optionally, a
50
+ * [`GroupLabel`](https://solid.ariakit.com/reference/group-label) can be rendered as
51
+ * a child to provide a label for the group.
52
+ * @see https://solid.ariakit.com/components/group
53
+ * @example
54
+ * ```jsx
55
+ * <Group>Group</Group>
56
+ * ```
57
+ */
58
+ export const Group = function Group(props: GroupProps) {
59
+ const htmlProps = useGroup(props);
60
+ return createInstance(TagName, htmlProps);
61
+ };
62
+
63
+ export type GroupOptions<_T extends ValidComponent = TagName> = Options;
64
+
65
+ export type GroupProps<T extends ValidComponent = TagName> = Props<
66
+ T,
67
+ GroupOptions<T>
68
+ >;
@@ -0,0 +1,5 @@
1
+ import type { Accessor } from "solid-js";
2
+ import { createContext } from "solid-js";
3
+ import type { HeadingLevels } from "./utils.ts";
4
+
5
+ export const HeadingContext = createContext<Accessor<HeadingLevels>>();
@@ -0,0 +1,43 @@
1
+ import type { JSX } from "solid-js";
2
+ import { useContext } from "solid-js";
3
+ import { HeadingContext } from "./heading-context.tsx";
4
+ import type { HeadingLevels } from "./utils.ts";
5
+
6
+ /**
7
+ * A component that sets the heading level for its children. It doesn't render
8
+ * any HTML element, just sets the
9
+ * [`level`](https://solid.ariakit.com/reference/heading-level#level) prop on the
10
+ * context.
11
+ * @see https://solid.ariakit.com/components/heading
12
+ * @example
13
+ * ```jsx
14
+ * <HeadingLevel>
15
+ * <Heading>Heading 1</Heading>
16
+ * <HeadingLevel>
17
+ * <Heading>Heading 2</Heading>
18
+ * </HeadingLevel>
19
+ * </HeadingLevel>
20
+ * ```
21
+ */
22
+ export function HeadingLevel(props: HeadingLevelProps) {
23
+ const contextLevel = useContext(HeadingContext);
24
+ const nextLevel = () =>
25
+ Math.max(
26
+ Math.min(props.level || (contextLevel?.() ?? 0) + 1, 6),
27
+ 1,
28
+ ) as HeadingLevels;
29
+ return (
30
+ <HeadingContext.Provider value={nextLevel}>
31
+ {props.children}
32
+ </HeadingContext.Provider>
33
+ );
34
+ }
35
+
36
+ export interface HeadingLevelProps {
37
+ /**
38
+ * The heading level. By default, it'll increase the level by 1 based on the
39
+ * context.
40
+ */
41
+ level?: HeadingLevels;
42
+ children?: JSX.Element;
43
+ }
@@ -0,0 +1,87 @@
1
+ import {
2
+ extractTagName,
3
+ createRef,
4
+ mergeProps,
5
+ createHook,
6
+ createInstance,
7
+ } from "@ariakit/solid-utils";
8
+ import type { Options, Props } from "@ariakit/solid-utils";
9
+ import type { ValidComponent } from "solid-js";
10
+ import { createMemo, useContext } from "solid-js";
11
+ import { HeadingContext } from "./heading-context.tsx";
12
+ import type { HeadingLevels } from "./utils.ts";
13
+
14
+ type HeadingElements = `h${HeadingLevels}`;
15
+ const TagName = "h1" satisfies ValidComponent;
16
+ type TagName = HeadingElements;
17
+ type HTMLType = HTMLElementTagNameMap[TagName];
18
+
19
+ /**
20
+ * Returns props to create a `Heading` component. The element type (or the
21
+ * `aria-level` prop, if the element type is not a native heading) is determined
22
+ * by the context level provided by the parent `HeadingLevel` component.
23
+ * @see https://solid.ariakit.com/components/heading
24
+ * @example
25
+ * ```jsx
26
+ * const props = useHeading();
27
+ * <Role {...props}>Heading</Role>
28
+ * ```
29
+ */
30
+ export const useHeading = createHook<TagName, HeadingOptions>(
31
+ function useHeading(props) {
32
+ const ref = createRef<HTMLType>();
33
+ const level = useContext(HeadingContext) || (() => 1);
34
+ const Element = () => `h${level()}` as const;
35
+ const tagName = extractTagName(ref.get);
36
+ const isNativeHeading = createMemo(
37
+ () => !!tagName() && /^h\d$/.test(tagName()!),
38
+ );
39
+
40
+ props = mergeProps(
41
+ {
42
+ // TODO: replace with LazyDynamic
43
+ render: Element(),
44
+ get role() {
45
+ return !isNativeHeading() ? "heading" : undefined;
46
+ },
47
+ get "aria-level"() {
48
+ return !isNativeHeading() ? level() : undefined;
49
+ },
50
+ ref: ref.set,
51
+ },
52
+ props,
53
+ );
54
+
55
+ return props;
56
+ },
57
+ );
58
+
59
+ /**
60
+ * Renders a heading element. The element type (or the `aria-level` attribute,
61
+ * if the element type is not a native heading) is determined by the context
62
+ * level provided by the closest
63
+ * [`HeadingLevel`](https://solid.ariakit.com/reference/heading-level) ancestor.
64
+ * @see https://solid.ariakit.com/components/heading
65
+ * @example
66
+ * ```jsx
67
+ * <HeadingLevel>
68
+ * <Heading>Heading 1</Heading>
69
+ * <HeadingLevel>
70
+ * <Heading>Heading 2</Heading>
71
+ * </HeadingLevel>
72
+ * </HeadingLevel>
73
+ * ```
74
+ */
75
+ export const Heading = function Heading(props: HeadingProps) {
76
+ const htmlProps = useHeading(props);
77
+ return createInstance(TagName, htmlProps);
78
+ };
79
+
80
+ export interface HeadingOptions<
81
+ _T extends ValidComponent = TagName,
82
+ > extends Options {}
83
+
84
+ export type HeadingProps<T extends ValidComponent = TagName> = Props<
85
+ T,
86
+ HeadingOptions<T>
87
+ >;
@@ -0,0 +1 @@
1
+ export type HeadingLevels = 1 | 2 | 3 | 4 | 5 | 6;
@@ -0,0 +1,91 @@
1
+ import { createHook, createInstance } from "@ariakit/solid-utils";
2
+ import type { Options, Props } from "@ariakit/solid-utils";
3
+ import type { Component, JSX, ValidComponent } from "solid-js";
4
+
5
+ const TagName = "div" satisfies ValidComponent;
6
+ type TagName = typeof TagName;
7
+
8
+ export const elements = [
9
+ "a",
10
+ "button",
11
+ "details",
12
+ "dialog",
13
+ "div",
14
+ "form",
15
+ "h1",
16
+ "h2",
17
+ "h3",
18
+ "h4",
19
+ "h5",
20
+ "h6",
21
+ "header",
22
+ "img",
23
+ "input",
24
+ "label",
25
+ "li",
26
+ "nav",
27
+ "ol",
28
+ "p",
29
+ "section",
30
+ "select",
31
+ "span",
32
+ "summary",
33
+ "textarea",
34
+ "ul",
35
+ "svg",
36
+ ] as const;
37
+
38
+ type RoleElements = {
39
+ [K in (typeof elements)[number]]: Component<RoleProps<K>>;
40
+ };
41
+
42
+ /**
43
+ * Returns props to create a `Role` component.
44
+ * @see https://solid.ariakit.com/components/role
45
+ * @example
46
+ * ```jsx
47
+ * const props = useRole();
48
+ * <Role {...props} />
49
+ * ```
50
+ */
51
+ export const useRole = createHook<TagName, RoleOptions>(
52
+ function useRole(props) {
53
+ return props;
54
+ },
55
+ );
56
+
57
+ // TODO: adapt docs wording to be more accurate for Solid
58
+ /**
59
+ * Renders an abstract element that supports the `render` prop and a
60
+ * `wrapInstance` prop that can be used to wrap the underlying component
61
+ * instance with Solid Portal, Context or other component types.
62
+ * @see https://solid.ariakit.com/components/role
63
+ * @example
64
+ * ```jsx
65
+ * <Role render={<As.div />} />
66
+ * ```
67
+ */
68
+ export const Role = function Role(props: RoleProps): JSX.Element {
69
+ return createInstance(TagName, props);
70
+ } as Component<RoleProps> & RoleElements;
71
+
72
+ Object.assign(
73
+ Role,
74
+ elements.reduce((acc, element) => {
75
+ acc[element] = function Role(
76
+ props: RoleProps<typeof element>,
77
+ ): JSX.Element {
78
+ return createInstance(element, props);
79
+ };
80
+ return acc;
81
+ }, {} as RoleElements),
82
+ );
83
+
84
+ export interface RoleOptions<
85
+ _T extends ValidComponent = TagName,
86
+ > extends Options {}
87
+
88
+ export type RoleProps<T extends ValidComponent = TagName> = Props<
89
+ T,
90
+ RoleOptions
91
+ >;
@@ -0,0 +1,66 @@
1
+ import {
2
+ mergeProps,
3
+ createHook,
4
+ createInstance,
5
+ withOptions,
6
+ } from "@ariakit/solid-utils";
7
+ import type { Options, Props } from "@ariakit/solid-utils";
8
+ import type { ValidComponent } from "solid-js";
9
+
10
+ const TagName = "hr" satisfies ValidComponent;
11
+ type TagName = typeof TagName;
12
+
13
+ /**
14
+ * Returns props to create a `Separator` component.
15
+ * @see https://solid.ariakit.com/components/separator
16
+ * @example
17
+ * ```jsx
18
+ * const props = useSeparator({ orientation: "horizontal" });
19
+ * <Role {...props} />
20
+ * ```
21
+ */
22
+ export const useSeparator = createHook<TagName, SeparatorOptions>(
23
+ withOptions(
24
+ { orientation: "horizontal" },
25
+ function useSeparator(props, options) {
26
+ props = mergeProps(
27
+ {
28
+ role: "separator" as const,
29
+ get "aria-orientation"() {
30
+ return options.orientation;
31
+ },
32
+ },
33
+ props,
34
+ );
35
+ return props;
36
+ },
37
+ ),
38
+ );
39
+
40
+ /**
41
+ * Renders a separator element.
42
+ * @see https://solid.ariakit.com/components/separator
43
+ * @example
44
+ * ```jsx
45
+ * <Separator orientation="horizontal" />
46
+ * ```
47
+ */
48
+ export const Separator = function Separator(props: SeparatorProps) {
49
+ const htmlProps = useSeparator(props);
50
+ return createInstance(TagName, htmlProps);
51
+ };
52
+
53
+ export interface SeparatorOptions<
54
+ _T extends ValidComponent = TagName,
55
+ > extends Options {
56
+ /**
57
+ * The orientation of the separator.
58
+ * @default "horizontal"
59
+ */
60
+ orientation?: "horizontal" | "vertical";
61
+ }
62
+
63
+ export type SeparatorProps<T extends ValidComponent = TagName> = Props<
64
+ T,
65
+ SeparatorOptions<T>
66
+ >;