@coopdigital/react 1.4.0 → 1.5.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 (96) hide show
  1. package/dist/components/AlertBanner/AlertBanner.d.ts +12 -2
  2. package/dist/components/AlertBanner/AlertBanner.js +3 -2
  3. package/dist/components/Author/Author.d.ts +7 -1
  4. package/dist/components/Author/Author.js +1 -0
  5. package/dist/components/Button/Button.d.ts +7 -1
  6. package/dist/components/Button/Button.js +1 -0
  7. package/dist/components/Card/Card.d.ts +40 -6
  8. package/dist/components/Card/Card.js +7 -6
  9. package/dist/components/Checkbox/Checkbox.d.ts +6 -4
  10. package/dist/components/Checkbox/Checkbox.js +9 -6
  11. package/dist/components/DatePicker/DatePicker.d.ts +8 -3
  12. package/dist/components/DatePicker/DatePicker.js +11 -11
  13. package/dist/components/Expandable/Expandable.d.ts +19 -3
  14. package/dist/components/Expandable/Expandable.js +4 -3
  15. package/dist/components/Field/Field.context.d.ts +14 -0
  16. package/dist/components/Field/Field.context.js +14 -0
  17. package/dist/components/Field/Field.d.ts +26 -4
  18. package/dist/components/Field/Field.js +33 -34
  19. package/dist/components/Field/index.js +1 -0
  20. package/dist/components/FieldMarkers/Label.js +4 -0
  21. package/dist/components/Fieldset/Fieldset.d.ts +33 -5
  22. package/dist/components/Fieldset/Fieldset.js +6 -5
  23. package/dist/components/Flourish/Flourish.d.ts +7 -1
  24. package/dist/components/Flourish/Flourish.js +1 -0
  25. package/dist/components/Form/Form.d.ts +7 -1
  26. package/dist/components/Form/Form.js +1 -0
  27. package/dist/components/Image/Image.d.ts +7 -1
  28. package/dist/components/Image/Image.js +1 -0
  29. package/dist/components/Pill/Pill.d.ts +12 -2
  30. package/dist/components/Pill/Pill.js +3 -2
  31. package/dist/components/Popover/Popover.d.ts +33 -5
  32. package/dist/components/Popover/Popover.js +6 -5
  33. package/dist/components/Popover/index.js +1 -0
  34. package/dist/components/Radio/Radio.d.ts +6 -4
  35. package/dist/components/Radio/Radio.js +9 -6
  36. package/dist/components/RootSVG/RootSVG.d.ts +7 -1
  37. package/dist/components/RootSVG/RootSVG.js +1 -0
  38. package/dist/components/Searchbox/Searchbox.context.d.ts +13 -0
  39. package/dist/components/Searchbox/Searchbox.context.js +11 -0
  40. package/dist/components/Searchbox/Searchbox.d.ts +15 -11
  41. package/dist/components/Searchbox/Searchbox.js +25 -28
  42. package/dist/components/Select/Select.d.ts +18 -5
  43. package/dist/components/Select/Select.js +9 -9
  44. package/dist/components/Signpost/Signpost.d.ts +7 -1
  45. package/dist/components/Signpost/Signpost.js +1 -0
  46. package/dist/components/SkipNav/SkipNav.d.ts +7 -1
  47. package/dist/components/SkipNav/SkipNav.js +1 -0
  48. package/dist/components/Squircle/Squircle.d.ts +7 -1
  49. package/dist/components/Squircle/Squircle.js +1 -0
  50. package/dist/components/Tag/Tag.d.ts +7 -1
  51. package/dist/components/Tag/Tag.js +1 -0
  52. package/dist/components/TextInput/TextInput.d.ts +4 -3
  53. package/dist/components/TextInput/TextInput.js +6 -6
  54. package/dist/components/Textarea/Textarea.d.ts +4 -3
  55. package/dist/components/Textarea/Textarea.js +4 -6
  56. package/dist/components/ValidationSummary/ValidationSummary.d.ts +26 -4
  57. package/dist/components/ValidationSummary/ValidationSummary.js +8 -6
  58. package/dist/hooks/useSlots.d.ts +4 -2
  59. package/dist/hooks/useSlots.js +33 -7
  60. package/dist/index.js +10 -10
  61. package/dist/types/index.d.ts +14 -14
  62. package/dist/utils/index.d.ts +1 -0
  63. package/dist/utils/index.js +9 -7
  64. package/package.json +9 -8
  65. package/src/components/AlertBanner/AlertBanner.tsx +5 -2
  66. package/src/components/Author/Author.tsx +4 -1
  67. package/src/components/Button/Button.tsx +5 -1
  68. package/src/components/Card/Card.tsx +8 -6
  69. package/src/components/Checkbox/Checkbox.tsx +13 -9
  70. package/src/components/DatePicker/DatePicker.tsx +15 -15
  71. package/src/components/Expandable/Expandable.tsx +5 -8
  72. package/src/components/Field/Field.context.ts +29 -0
  73. package/src/components/Field/Field.tsx +61 -56
  74. package/src/components/FieldMarkers/Label.tsx +4 -0
  75. package/src/components/Fieldset/Fieldset.tsx +7 -5
  76. package/src/components/Flourish/Flourish.tsx +4 -1
  77. package/src/components/Form/Form.tsx +4 -1
  78. package/src/components/Image/Image.tsx +4 -1
  79. package/src/components/Pill/Pill.tsx +4 -2
  80. package/src/components/Popover/Popover.tsx +7 -5
  81. package/src/components/Radio/Radio.tsx +12 -9
  82. package/src/components/RootSVG/RootSVG.tsx +4 -1
  83. package/src/components/Searchbox/Searchbox.context.tsx +20 -0
  84. package/src/components/Searchbox/Searchbox.tsx +55 -44
  85. package/src/components/Select/Select.tsx +11 -11
  86. package/src/components/Signpost/Signpost.tsx +4 -1
  87. package/src/components/SkipNav/SkipNav.tsx +4 -1
  88. package/src/components/Squircle/Squircle.tsx +4 -1
  89. package/src/components/Tag/Tag.tsx +4 -1
  90. package/src/components/TextInput/TextInput.tsx +8 -8
  91. package/src/components/Textarea/Textarea.tsx +7 -8
  92. package/src/components/ValidationSummary/ValidationSummary.tsx +12 -5
  93. package/src/hooks/useSlots.ts +37 -9
  94. package/src/types/index.ts +18 -14
  95. package/src/utils/index.ts +8 -6
  96. package/dist/node_modules/merge-refs/dist/index.js +0 -36
@@ -1,6 +1,6 @@
1
1
  "use client"
2
2
 
3
- import type { FormHTMLAttributes, JSX, Ref } from "react"
3
+ import type { FormHTMLAttributes, JSX, RefObject } from "react"
4
4
 
5
5
  import clsx from "clsx"
6
6
  import React, { useCallback, useState } from "react"
@@ -8,11 +8,13 @@ import React, { useCallback, useState } from "react"
8
8
  import { useId } from "../../hooks/useId"
9
9
  import { useSlots } from "../../hooks/useSlots"
10
10
  import { StandardSizes } from "../../types"
11
- import { hasUserBorder } from "../../utils"
11
+ import { getSlotProp, hasUserBorder } from "../../utils"
12
12
  import { Button, type ButtonProps } from "../Button"
13
13
  import Field from "../Field"
14
+ import { FieldLabel } from "../Field/Field"
14
15
  import { SearchIcon } from "../icons"
15
16
  import TextInput, { TextInputProps } from "../TextInput"
17
+ import { SearchboxContext, useSearchbox } from "./Searchbox.context"
16
18
 
17
19
  export interface SearchboxProps extends FormHTMLAttributes<HTMLFormElement> {
18
20
  /** **(Optional)** Specify a server endpoint to submit the form. Will be ignored if onSubmit is also set. */
@@ -34,7 +36,7 @@ export interface SearchboxProps extends FormHTMLAttributes<HTMLFormElement> {
34
36
  /** **(Optional)** Specify the TextInput placeholder text Do not use in place of a form label. */
35
37
  placeholder?: string
36
38
  /** **(Optional)** Specify a custom React ref for this component. */
37
- ref?: Ref<HTMLFormElement>
39
+ ref?: RefObject<HTMLFormElement | null>
38
40
  /** **(Optional)** Specify the Searchbox size. */
39
41
  size?: StandardSizes
40
42
  /** **(Optional)** Specify the Searchbox variant. */
@@ -46,7 +48,7 @@ const componentSlots = {
46
48
  SearchboxInput: null,
47
49
  }
48
50
 
49
- export const Searchbox = ({
51
+ const Searchbox = ({
50
52
  action,
51
53
  children,
52
54
  className,
@@ -59,10 +61,13 @@ export const Searchbox = ({
59
61
  variant = "solid",
60
62
  ...props
61
63
  }: SearchboxProps): JSX.Element => {
62
- const slots = { ...useSlots(componentSlots, children) }
64
+ const slots = useSlots(componentSlots, children)
65
+ const uid = useId(id)
63
66
 
64
67
  const [isPending, setIsPending] = useState(false)
65
- const uid = useId(id)
68
+
69
+ const inputId = getSlotProp<string>(slots.SearchboxInput, "id") ?? uid + "__input"
70
+ const buttonId = getSlotProp<string>(slots.SearchboxButton, "id") ?? uid + "__button"
66
71
 
67
72
  const handleSubmit = useCallback(
68
73
  async (event: React.SubmitEvent<HTMLFormElement>) => {
@@ -91,63 +96,69 @@ export const Searchbox = ({
91
96
  className: clsx("coop-searchbox", borderClass, className),
92
97
  "data-size": size ?? undefined,
93
98
  "data-variant": variant.length && variant !== "solid" ? variant : undefined,
94
- id: uid + "__form",
99
+ id: id ?? undefined,
95
100
  onSubmit: onSubmit ? handleSubmit : undefined,
96
101
  ...props,
97
102
  }
98
103
 
99
- slots.SearchboxButton = React.cloneElement(
100
- slots.SearchboxButton as React.ReactElement<ButtonProps>,
101
- {
102
- isLoading: isPending,
103
- size,
104
- }
105
- )
106
-
107
- slots.SearchboxInput = React.cloneElement(
108
- slots.SearchboxInput as React.ReactElement<TextInputProps>,
109
- {
110
- autoCapitalize: "off",
111
- id: uid,
112
- size,
113
- type: "search",
114
- }
115
- )
116
-
117
- const labelProps = {
118
- htmlFor: uid,
119
- isVisible: labelVisible,
120
- }
121
-
122
104
  return (
123
- <form {...formProps} ref={ref}>
124
- {label && <Field.Label {...labelProps}>{label}</Field.Label>}
125
- <div className={clsx("coop-searchbox--inner")}>
126
- <Field>{slots.SearchboxInput}</Field>
127
- {slots.SearchboxButton}
128
- </div>
129
- </form>
105
+ <SearchboxContext.Provider value={{ buttonId, inputId, isLoading: isPending, ref, size }}>
106
+ <form {...formProps} ref={ref}>
107
+ {label && (
108
+ <FieldLabel htmlFor={inputId ?? uid} isVisible={labelVisible}>
109
+ {label}
110
+ </FieldLabel>
111
+ )}
112
+ <div className={clsx("coop-searchbox--inner")}>
113
+ <Field>{slots.SearchboxInput}</Field>
114
+ {slots.SearchboxButton}
115
+ </div>
116
+ </form>
117
+ </SearchboxContext.Provider>
130
118
  )
131
119
  }
132
120
 
133
- const SearchboxButton = ({ children, id, ...props }: ButtonProps) => {
134
- const uid = useId(id)
121
+ const SearchboxButton = ({ children, ...props }: ButtonProps) => {
122
+ // const uid = useId(id)
123
+ const searchbox = useSearchbox()
135
124
  const buttonProps = {
136
125
  children: children ?? (
137
- <SearchIcon alt="Search" id={uid + "__icon"} stroke="currentColor" width="24" />
126
+ <SearchIcon
127
+ alt="Search"
128
+ id={searchbox.buttonId + "__icon"}
129
+ stroke="currentColor"
130
+ width="24"
131
+ />
138
132
  ),
133
+ id: searchbox.buttonId,
134
+ isLoading: searchbox.isLoading,
135
+ size: searchbox.size,
139
136
  ...props,
140
137
  }
141
138
 
142
139
  return <Button {...buttonProps} />
143
140
  }
144
141
 
145
- const SearchboxInput = (props: TextInputProps) => <TextInput {...props} />
142
+ SearchboxButton.displayName = "Searchbox.Button"
143
+
144
+ const SearchboxInput = (props: TextInputProps) => {
145
+ const searchbox = useSearchbox()
146
+
147
+ return (
148
+ <TextInput
149
+ autoCapitalize="off"
150
+ id={searchbox.inputId}
151
+ size={searchbox.size}
152
+ type="search"
153
+ {...props}
154
+ />
155
+ )
156
+ }
157
+
158
+ SearchboxInput.displayName = "Searchbox.Input"
146
159
 
147
160
  Searchbox.Button = SearchboxButton
148
161
  Searchbox.Input = SearchboxInput
149
162
 
150
- SearchboxButton.config = { name: "SearchboxButton" }
151
- SearchboxInput.config = { isField: true, name: "SearchboxInput" }
152
-
153
163
  export default Searchbox
164
+ export { Searchbox, SearchboxButton, SearchboxInput }
@@ -1,3 +1,4 @@
1
+ "use client"
1
2
  import type {
2
3
  JSX,
3
4
  OptgroupHTMLAttributes,
@@ -9,8 +10,8 @@ import type {
9
10
 
10
11
  import clsx from "clsx"
11
12
 
12
- import { ComponentConfig, StandardSizes } from "../../../src/types"
13
- import { useId } from "../../hooks/useId"
13
+ import { StandardSizes } from "../../../src/types"
14
+ import { useField } from "../Field/Field.context"
14
15
 
15
16
  export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "size"> {
16
17
  /** **(Optional)** Options inside the Select. This should be a set of `Select.Option` or `Select.OptionGroup` components. */
@@ -47,7 +48,7 @@ interface SelectOptionProps extends OptionHTMLAttributes<HTMLOptionElement> {
47
48
  value: string
48
49
  }
49
50
 
50
- export const Select = ({
51
+ const Select = ({
51
52
  children,
52
53
  className,
53
54
  disabled,
@@ -58,13 +59,14 @@ export const Select = ({
58
59
  size,
59
60
  ...props
60
61
  }: SelectProps): JSX.Element => {
61
- const uid = useId(id)
62
+ const field = useField()
63
+
62
64
  const componentProps = {
63
65
  className: clsx("coop-select", "coop-field-element", className),
64
66
  "data-error": error ?? undefined,
65
67
  "data-size": size ?? undefined,
66
68
  disabled,
67
- id: uid,
69
+ id: id ?? field?.id,
68
70
  name,
69
71
  ...props,
70
72
  }
@@ -88,14 +90,12 @@ const SelectOption = ({ children, className, ...props }: SelectOptionProps): JSX
88
90
  )
89
91
  }
90
92
 
91
- SelectOption.config = { name: "SelectOption" }
92
- SelectOptionGroup.config = { name: "SelectOptionGroup" }
93
+ Select.slot = { name: "Select" }
94
+ SelectOption.slot = { name: "SelectOption" }
95
+ SelectOptionGroup.slot = { name: "SelectOptionGroup" }
93
96
 
94
97
  Select.OptionGroup = SelectOptionGroup
95
98
  Select.Option = SelectOption
96
99
 
97
- Select.config = {
98
- isField: true,
99
- } satisfies ComponentConfig
100
-
101
100
  export default Select
101
+ export { Select, SelectOption, SelectOptionGroup }
@@ -24,7 +24,7 @@ export interface SignpostProps extends HTMLAttributes<HTMLDivElement> {
24
24
  ref?: Ref<HTMLDivElement>
25
25
  }
26
26
 
27
- export const Signpost = ({
27
+ const Signpost = ({
28
28
  as,
29
29
  children,
30
30
  className,
@@ -58,4 +58,7 @@ export const Signpost = ({
58
58
  )
59
59
  }
60
60
 
61
+ Signpost.slot = { name: "Signpost" }
62
+
61
63
  export default Signpost
64
+ export { Signpost }
@@ -21,7 +21,7 @@ export interface SkipNavProps extends HTMLAttributes<HTMLDivElement> {
21
21
 
22
22
  const defaultLinks = [{ href: "#main", label: "Skip to main content" }]
23
23
 
24
- export const SkipNav = ({
24
+ const SkipNav = ({
25
25
  className,
26
26
  isVisible = false,
27
27
  links = defaultLinks,
@@ -53,4 +53,7 @@ export const SkipNav = ({
53
53
  )
54
54
  }
55
55
 
56
+ SkipNav.slot = { name: "SkipNav" }
57
+
56
58
  export default SkipNav
59
+ export { SkipNav }
@@ -14,7 +14,7 @@ export interface SquircleProps extends HTMLAttributes<HTMLDivElement> {
14
14
  size?: "sm" | "md" | "lg"
15
15
  }
16
16
 
17
- export const Squircle = ({
17
+ const Squircle = ({
18
18
  children,
19
19
  className,
20
20
  ref,
@@ -34,4 +34,7 @@ export const Squircle = ({
34
34
  )
35
35
  }
36
36
 
37
+ Squircle.slot = { name: "Squircle" }
38
+
37
39
  export default Squircle
40
+ export { Squircle }
@@ -21,7 +21,7 @@ export interface TagProps extends HTMLAttributes<HTMLAnchorElement> {
21
21
  size?: "sm" | "md"
22
22
  }
23
23
 
24
- export const Tag = ({
24
+ const Tag = ({
25
25
  as,
26
26
  children,
27
27
  className,
@@ -42,4 +42,7 @@ export const Tag = ({
42
42
  return React.createElement(element, { ...componentProps, ref }, children)
43
43
  }
44
44
 
45
+ Tag.slot = { name: "Tag" }
46
+
45
47
  export default Tag
48
+ export { Tag }
@@ -1,9 +1,10 @@
1
+ "use client"
1
2
  import type { InputHTMLAttributes, JSX, Ref } from "react"
2
3
 
3
4
  import clsx from "clsx"
4
5
 
5
- import { ComponentConfig, StandardSizes } from "../../../src/types"
6
- import { useId } from "../../hooks/useId"
6
+ import { StandardSizes } from "../../../src/types"
7
+ import { useField } from "../Field/Field.context"
7
8
 
8
9
  export interface TextInputProps extends Omit<
9
10
  InputHTMLAttributes<HTMLInputElement>,
@@ -49,7 +50,7 @@ export interface TextInputProps extends Omit<
49
50
  | "time"
50
51
  }
51
52
 
52
- export const TextInput = ({
53
+ const TextInput = ({
53
54
  "aria-placeholder": ariaPlaceholder,
54
55
  className,
55
56
  disabled,
@@ -66,7 +67,7 @@ export const TextInput = ({
66
67
  type = "text",
67
68
  ...props
68
69
  }: TextInputProps): JSX.Element => {
69
- const uid = useId(id)
70
+ const field = useField()
70
71
 
71
72
  const componentProps = {
72
73
  "aria-placeholder": placeholder ?? ariaPlaceholder ?? undefined,
@@ -74,7 +75,7 @@ export const TextInput = ({
74
75
  "data-error": error || undefined,
75
76
  "data-size": size?.length ? size : undefined,
76
77
  disabled,
77
- id: uid,
78
+ id: id ?? field?.id,
78
79
  name,
79
80
  placeholder,
80
81
  type,
@@ -100,8 +101,7 @@ export const TextInput = ({
100
101
  )
101
102
  }
102
103
 
103
- TextInput.config = {
104
- isField: true,
105
- } satisfies ComponentConfig
104
+ TextInput.slot = { name: "TextInput" }
106
105
 
107
106
  export default TextInput
107
+ export { TextInput }
@@ -6,8 +6,8 @@ import clsx from "clsx"
6
6
  import { useState } from "react"
7
7
 
8
8
  import { useDebounce } from "../../hooks/useDebounce"
9
- import { useId } from "../../hooks/useId"
10
- import { ComponentConfig, StandardSizes } from "../../types"
9
+ import { StandardSizes } from "../../types"
10
+ import { useField } from "../Field/Field.context"
11
11
 
12
12
  const DEBOUNCE_DELAY = 750
13
13
 
@@ -47,7 +47,7 @@ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElemen
47
47
  size?: StandardSizes
48
48
  }
49
49
 
50
- export const Textarea = ({
50
+ const Textarea = ({
51
51
  "aria-placeholder": ariaPlaceholder,
52
52
  className,
53
53
  cols = 30,
@@ -65,7 +65,7 @@ export const Textarea = ({
65
65
  size,
66
66
  ...props
67
67
  }: TextareaProps): JSX.Element => {
68
- const uid = useId(id)
68
+ const field = useField()
69
69
 
70
70
  const componentProps = {
71
71
  "aria-placeholder": placeholder ?? ariaPlaceholder ?? undefined,
@@ -74,7 +74,7 @@ export const Textarea = ({
74
74
  "data-error": error ?? undefined,
75
75
  "data-size": size ?? undefined,
76
76
  disabled,
77
- id: uid,
77
+ id: id ?? field?.id,
78
78
  maxLength: cutoff ? maxLength : undefined,
79
79
  name,
80
80
  placeholder,
@@ -113,8 +113,7 @@ export const Textarea = ({
113
113
  )
114
114
  }
115
115
 
116
- Textarea.config = {
117
- isField: true,
118
- } satisfies ComponentConfig
116
+ Textarea.slot = { name: "Textarea" }
119
117
 
120
118
  export default Textarea
119
+ export { Textarea }
@@ -1,5 +1,5 @@
1
- /* eslint-disable jsx-a11y/anchor-is-valid */
2
1
  "use client"
2
+ /* eslint-disable jsx-a11y/anchor-is-valid */
3
3
 
4
4
  import type { HTMLAttributes, JSX, ReactNode, Ref } from "react"
5
5
 
@@ -70,7 +70,7 @@ const componentSlots = {
70
70
  ValidationSummaryPrompt: null,
71
71
  }
72
72
 
73
- export const ValidationSummary = ({
73
+ const ValidationSummary = ({
74
74
  autoScroll = true,
75
75
  children,
76
76
  className,
@@ -178,12 +178,19 @@ const ValidationSummaryErrors = ({ errors }: ValidationSummaryErrorsProps): JSX.
178
178
  ) : null
179
179
  }
180
180
 
181
- ValidationSummaryHeading.config = { name: "ValidationSummaryHeading" }
182
- ValidationSummaryPrompt.config = { name: "ValidationSummaryPrompt" }
183
- ValidationSummaryErrors.config = { name: "ValidationSummaryErrors" }
181
+ ValidationSummary.slot = { name: "ValidationSummary" }
182
+ ValidationSummaryHeading.slot = { name: "ValidationSummaryHeading" }
183
+ ValidationSummaryPrompt.slot = { name: "ValidationSummaryPrompt" }
184
+ ValidationSummaryErrors.slot = { name: "ValidationSummaryErrors" }
184
185
 
185
186
  ValidationSummary.Heading = ValidationSummaryHeading
186
187
  ValidationSummary.Prompt = ValidationSummaryPrompt
187
188
  ValidationSummary.Errors = ValidationSummaryErrors
188
189
 
189
190
  export default ValidationSummary
191
+ export {
192
+ ValidationSummary,
193
+ ValidationSummaryErrors,
194
+ ValidationSummaryHeading,
195
+ ValidationSummaryPrompt,
196
+ }
@@ -1,6 +1,6 @@
1
1
  import React from "react"
2
2
 
3
- import { ReactNodeWithConfig } from "../types"
3
+ import { LazyComponent, SlotConfig } from "../types"
4
4
 
5
5
  type Slots<T> = Record<keyof T, React.ReactNode>
6
6
 
@@ -8,22 +8,50 @@ export function isKey<T extends object>(x: T, k: PropertyKey): k is keyof T {
8
8
  return k in x
9
9
  }
10
10
 
11
- export function getSlotName(node: ReactNodeWithConfig): string | false {
12
- if (
13
- React.isValidElement(node) &&
14
- node.type &&
15
- typeof node.type !== "string" &&
16
- typeof node.type !== "symbol"
17
- ) {
18
- return node.type?.config?.name ?? node.type.name
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ export function isLazy<T extends React.ComponentType<any>>(
13
+ type: unknown
14
+ ): type is LazyComponent<T> {
15
+ return typeof type === "object" && type !== null && "_payload" in type && "_init" in type
16
+ }
17
+
18
+ export function resolveComponentName(child: React.ReactNode): string | null {
19
+ if (child && typeof child === "object" && "type" in child) {
20
+ const type = child.type
21
+
22
+ if (isLazy(type)) {
23
+ try {
24
+ const resolved = type._init(type._payload)
25
+ if ("slot" in resolved) return (resolved.slot as SlotConfig).name
26
+ return resolved.displayName ?? resolved.name ?? null
27
+ } catch {
28
+ // _init throws a Promise if the payload is still pending
29
+ return null
30
+ }
31
+ }
32
+
33
+ if (typeof type === "function") {
34
+ if ("slot" in type) return (type.slot as SlotConfig).name
35
+ return (type as React.FC).displayName ?? (type as React.FC).name ?? null
36
+ }
19
37
  }
38
+ return null
39
+ }
40
+
41
+ export function getSlotName(node: React.ReactNode): string | false {
42
+ const slotName = resolveComponentName(node)
20
43
 
44
+ if (slotName) {
45
+ return slotName.replaceAll(".", "")
46
+ // return root && !safeName.startsWith(root) ? root + safeName : safeName
47
+ }
21
48
  return false
22
49
  }
23
50
 
24
51
  export function useSlots<T>(
25
52
  componentSlots: Slots<T>,
26
53
  children: React.ReactNode,
54
+
27
55
  options?: { collect?: string[] }
28
56
  ): Slots<T> {
29
57
  return React.Children.toArray(children).reduce(
@@ -1,19 +1,23 @@
1
1
  export type StandardSizes = "sm" | "md" | "lg"
2
2
 
3
- export type FormFieldError = { message?: string; type?: string } | boolean | null | undefined
3
+ // export type FormFieldError = { message?: string; type?: string } | boolean | null | undefined
4
4
 
5
- export type ReactNodeWithConfig =
6
- | (React.ReactNode & {
7
- type?: {
8
- config?: ComponentConfig
9
- }
10
- })
11
- | undefined
12
- | null
5
+ export interface SlotConfig {
6
+ field?: boolean
7
+ inline?: boolean
8
+ name: string
9
+ suffix?: string
10
+ }
11
+
12
+ export interface LazyPayload<T> {
13
+ default: T
14
+ }
13
15
 
14
- export interface ComponentConfig {
15
- fieldSuffix?: string
16
- isField?: boolean
17
- isInline?: boolean
18
- name?: string
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ export interface LazyComponent<T extends React.ComponentType<any>> {
18
+ _init: (payload: unknown) => T
19
+ _payload: Promise<LazyPayload<T>> | LazyPayload<T>
20
+ $$typeof: typeof REACT_LAZY_TYPE
19
21
  }
22
+
23
+ export const REACT_LAZY_TYPE = Symbol.for("react.lazy")
@@ -1,3 +1,5 @@
1
+ import { isValidElement } from "react"
2
+
1
3
  export const bgPropToClass = (color: string, userClasses?: string) =>
2
4
  userClasses?.includes("bg-") ? "" : `bg-${color}`
3
5
 
@@ -11,11 +13,11 @@ export const hasUserBorder = (userClasses?: string) => {
11
13
 
12
14
  export const bgClassToColor = (userClasses: string) => {
13
15
  return /bg-([^\s]+)/.exec(userClasses)?.[1] ?? null
16
+ }
14
17
 
15
- // return (
16
- // userClasses
17
- // .split(" ")
18
- // .find((c) => c.startsWith("bg"))
19
- // ?.replace("bg-", "") ?? ""
20
- // )
18
+ export function getSlotProp<R = unknown>(slot: React.ReactNode, prop: string): R | undefined {
19
+ if (isValidElement(slot) && prop in (slot.props as object)) {
20
+ return (slot.props as Record<string, unknown>)[prop] as R
21
+ }
22
+ return undefined
21
23
  }
@@ -1,36 +0,0 @@
1
- /**
2
- * A function that merges React refs into one.
3
- * Supports both functions and ref objects created using createRef() and useRef().
4
- *
5
- * Usage:
6
- * ```tsx
7
- * <div ref={mergeRefs(ref1, ref2, ref3)} />
8
- * ```
9
- *
10
- * @param {(React.Ref<T> | undefined)[]} inputRefs Array of refs
11
- * @returns {React.Ref<T> | React.RefCallback<T>} Merged refs
12
- */
13
- function mergeRefs() {
14
- var inputRefs = [];
15
- for (var _i = 0; _i < arguments.length; _i++) {
16
- inputRefs[_i] = arguments[_i];
17
- }
18
- var filteredInputRefs = inputRefs.filter(Boolean);
19
- if (filteredInputRefs.length <= 1) {
20
- var firstRef = filteredInputRefs[0];
21
- return firstRef || null;
22
- }
23
- return function mergedRefs(ref) {
24
- for (var _i = 0, filteredInputRefs_1 = filteredInputRefs; _i < filteredInputRefs_1.length; _i++) {
25
- var inputRef = filteredInputRefs_1[_i];
26
- if (typeof inputRef === 'function') {
27
- inputRef(ref);
28
- }
29
- else if (inputRef) {
30
- inputRef.current = ref;
31
- }
32
- }
33
- };
34
- }
35
-
36
- export { mergeRefs as default };