@kalink-ui/seedly 0.9.0 → 0.11.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 (108) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +14 -6
  3. package/src/components/box/box.css.ts +2 -2
  4. package/src/components/button/button.css.ts +52 -50
  5. package/src/components/button/button.tsx +15 -12
  6. package/src/components/button/index.ts +1 -1
  7. package/src/components/button-icon/button-icon.css.ts +90 -0
  8. package/src/components/button-icon/button-icon.tsx +23 -0
  9. package/src/components/button-icon/index.ts +1 -0
  10. package/src/components/card/card.tsx +7 -4
  11. package/src/components/card/index.ts +1 -1
  12. package/src/components/center/center.tsx +2 -2
  13. package/src/components/center/index.ts +1 -1
  14. package/src/components/cluster/cluster.css.ts +17 -0
  15. package/src/components/command/command-empty.tsx +14 -0
  16. package/src/components/command/command-group.css.ts +34 -0
  17. package/src/components/command/command-group.tsx +19 -0
  18. package/src/components/command/command-input.css.ts +31 -0
  19. package/src/components/command/command-input.tsx +44 -0
  20. package/src/components/command/command-item.css.ts +27 -0
  21. package/src/components/command/command-item.tsx +35 -0
  22. package/src/components/command/command-list.css.ts +14 -0
  23. package/src/components/command/command-list.tsx +19 -0
  24. package/src/components/command/command-separator.tsx +29 -0
  25. package/src/components/command/command.tsx +24 -0
  26. package/src/components/command/index.ts +7 -0
  27. package/src/components/cover/index.ts +1 -1
  28. package/src/components/divider/divider.css.ts +11 -0
  29. package/src/components/divider/divider.tsx +11 -0
  30. package/src/components/divider/index.ts +1 -0
  31. package/src/components/form-field/form-field-context.ts +18 -0
  32. package/src/components/form-field/form-field-control.tsx +34 -0
  33. package/src/components/form-field/form-field-description.tsx +16 -0
  34. package/src/components/form-field/form-field-error.tsx +22 -0
  35. package/src/components/form-field/form-field-item-context.ts +6 -0
  36. package/src/components/form-field/form-field-item.tsx +28 -0
  37. package/src/components/form-field/form-field-label.tsx +27 -0
  38. package/src/components/form-field/form-field-message.tsx +33 -0
  39. package/src/components/form-field/form-field.css.ts +97 -0
  40. package/src/components/form-field/form-field.tsx +56 -0
  41. package/src/components/form-field/index.ts +9 -0
  42. package/src/components/frame/frame.css.ts +8 -8
  43. package/src/components/frame/frame.tsx +2 -6
  44. package/src/components/frame/index.ts +1 -1
  45. package/src/components/heading/heading.tsx +43 -8
  46. package/src/components/index.ts +29 -15
  47. package/src/components/input/index.ts +2 -0
  48. package/src/components/input/input-wrapper.tsx +58 -0
  49. package/src/components/input/input.css.ts +250 -0
  50. package/src/components/input/input.tsx +56 -0
  51. package/src/components/label/index.ts +1 -0
  52. package/src/components/label/label.css.ts +37 -0
  53. package/src/components/label/label.tsx +23 -0
  54. package/src/components/loader/index.ts +1 -0
  55. package/src/components/loader/loader.css.ts +109 -0
  56. package/src/components/loader/moon-loader.tsx +43 -0
  57. package/src/components/loader-overlay/index.ts +1 -0
  58. package/src/components/loader-overlay/loader-overlay.css.ts +35 -0
  59. package/src/components/loader-overlay/loader-overlay.tsx +28 -0
  60. package/src/components/menu/index.ts +2 -0
  61. package/src/components/menu/menu-item.css.ts +79 -0
  62. package/src/components/menu/menu-separator.css.ts +53 -0
  63. package/src/components/popover/index.ts +3 -0
  64. package/src/components/popover/popover-content.css.ts +107 -0
  65. package/src/components/popover/popover-content.tsx +82 -0
  66. package/src/components/popover/popover.tsx +6 -0
  67. package/src/components/scroll-area/index.ts +1 -0
  68. package/src/components/scroll-area/scroll-area.css.ts +72 -0
  69. package/src/components/scroll-area/scroll-area.tsx +39 -0
  70. package/src/components/scroll-area/scroll-bar.tsx +37 -0
  71. package/src/components/seed/seed.tsx +4 -4
  72. package/src/components/select/index.ts +5 -0
  73. package/src/components/select/select-content.css.ts +22 -0
  74. package/src/components/select/select-content.tsx +51 -0
  75. package/src/components/select/select-item.css.ts +24 -0
  76. package/src/components/select/select-item.tsx +24 -0
  77. package/src/components/select/select-root.tsx +14 -0
  78. package/src/components/select/select-trigger.css.ts +75 -0
  79. package/src/components/select/select-trigger.tsx +47 -0
  80. package/src/components/select/select.tsx +85 -0
  81. package/src/components/sheet/index.ts +5 -0
  82. package/src/components/sheet/sheet-content.css.ts +143 -0
  83. package/src/components/sheet/sheet-content.tsx +43 -0
  84. package/src/components/sheet/sheet-description.tsx +21 -0
  85. package/src/components/sheet/sheet-footer.tsx +15 -0
  86. package/src/components/sheet/sheet-header.css.ts +35 -0
  87. package/src/components/sheet/sheet-header.tsx +32 -0
  88. package/src/components/sheet/sheet-overlay.css.ts +43 -0
  89. package/src/components/sheet/sheet-overlay.tsx +14 -0
  90. package/src/components/sheet/sheet-title.tsx +31 -0
  91. package/src/components/sheet/sheet.tsx +8 -0
  92. package/src/components/stack/index.ts +1 -1
  93. package/src/components/stack/stack.css.ts +5 -1
  94. package/src/components/stack/stack.tsx +2 -2
  95. package/src/components/text/index.ts +6 -0
  96. package/src/components/text/text.css.ts +31 -4
  97. package/src/components/text-field/index.ts +1 -0
  98. package/src/components/text-field/text-field.css.ts +3 -0
  99. package/src/components/text-field/text-field.tsx +64 -0
  100. package/src/components/textarea/index.ts +1 -0
  101. package/src/components/textarea/textarea-input.tsx +20 -0
  102. package/src/components/textarea/textarea.css.ts +10 -0
  103. package/src/components/textarea/textarea.tsx +69 -0
  104. package/src/styles/define-responsive-properties.ts +242 -0
  105. package/src/styles/extract-sprinkles-props.ts +29 -35
  106. package/src/styles/index.ts +9 -0
  107. package/src/styles/reset.css.ts +1 -0
  108. package/src/styles/visually-hidden.css.ts +21 -0
@@ -0,0 +1,31 @@
1
+ import { createVar, style } from '@vanilla-extract/css';
2
+
3
+ import { sys, transition } from '../../styles';
4
+
5
+ const outlineColor = createVar();
6
+
7
+ export const commandInputWrapper = style({
8
+ padding: sys.spacing[2],
9
+
10
+ borderBottomWidth: 1,
11
+ borderBottomStyle: 'solid',
12
+ borderBottomColor: outlineColor,
13
+
14
+ transition: transition(['border-color', 'box-shadow'], {
15
+ duration: 'short.2',
16
+ }),
17
+
18
+ selectors: {
19
+ '&:focus, &:focus-within, &:focus-visible': {
20
+ boxShadow: 'unset',
21
+
22
+ vars: {
23
+ [outlineColor]: sys.color.foreground,
24
+ },
25
+ },
26
+ },
27
+
28
+ vars: {
29
+ [outlineColor]: sys.color.foreground,
30
+ },
31
+ });
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { mergeRefs } from '@kalink-ui/dibbly';
4
+ import { clsx } from 'clsx';
5
+ import { Command as CommandPrimitive } from 'cmdk';
6
+ import { ComponentPropsWithRef, ReactNode, useRef } from 'react';
7
+
8
+ import { InputWrapper } from '../input/input-wrapper';
9
+ import { input } from '../input/input.css';
10
+
11
+ import { commandInputWrapper } from './command-input.css';
12
+
13
+ export type CommandInputProps = ComponentPropsWithRef<
14
+ typeof CommandPrimitive.Input
15
+ > & {
16
+ icon?: ReactNode;
17
+ };
18
+
19
+ export function CommandInput({
20
+ className,
21
+ disabled,
22
+ ref,
23
+ icon,
24
+ ...props
25
+ }: CommandInputProps) {
26
+ const innerRef = useRef<HTMLInputElement>(null);
27
+
28
+ return (
29
+ <InputWrapper
30
+ inputRef={innerRef}
31
+ disabled={disabled}
32
+ variant="bare"
33
+ className={commandInputWrapper}
34
+ >
35
+ {icon}
36
+ <CommandPrimitive.Input
37
+ ref={mergeRefs([ref, innerRef])}
38
+ className={clsx(input, className)}
39
+ disabled={disabled}
40
+ {...props}
41
+ />
42
+ </InputWrapper>
43
+ );
44
+ }
@@ -0,0 +1,27 @@
1
+ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
2
+
3
+ import { sys } from '../../styles';
4
+
5
+ export const commandItem = recipe({
6
+ base: {
7
+ cursor: 'pointer',
8
+
9
+ color: sys.color.foreground,
10
+
11
+ selectors: {
12
+ '&[data-selected=true]': {
13
+ backgroundColor: `color-mix(in srgb, ${sys.color.foreground} calc(${sys.state.muted.dark} * 100%), transparent)`,
14
+ outline: 'none',
15
+ },
16
+
17
+ '&:active': {
18
+ backgroundColor: `color-mix(in srgb, ${sys.color.foreground} calc(${sys.state.focused} * 100%), transparent)`,
19
+ outline: 'none',
20
+ },
21
+ },
22
+ },
23
+ });
24
+
25
+ export type CommandItemVariants = NonNullable<
26
+ RecipeVariants<typeof commandItem>
27
+ >;
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+
3
+ import { clsx } from 'clsx';
4
+ import { Command as CommandPrimitive } from 'cmdk';
5
+ import { ComponentPropsWithRef, ComponentType } from 'react';
6
+
7
+ import { Cluster } from '../cluster';
8
+ import { menuItem, menuItemIcon } from '../menu/menu-item.css';
9
+
10
+ export type CommandItemProps = ComponentPropsWithRef<
11
+ typeof CommandPrimitive.Item
12
+ > & {
13
+ inset?: boolean;
14
+ icon?: ComponentType<{ className?: string }>;
15
+ };
16
+
17
+ export function CommandItem({
18
+ className,
19
+ inset,
20
+ icon: IconComp,
21
+ children,
22
+ ...props
23
+ }: CommandItemProps) {
24
+ return (
25
+ <CommandPrimitive.Item
26
+ className={clsx(menuItem({ inset }), className)}
27
+ {...props}
28
+ >
29
+ <Cluster spacing={2} align="center">
30
+ {IconComp && <IconComp className={menuItemIcon} />}
31
+ {children}
32
+ </Cluster>
33
+ </CommandPrimitive.Item>
34
+ );
35
+ }
@@ -0,0 +1,14 @@
1
+ import { style } from '@vanilla-extract/css';
2
+
3
+ import { sys, transition } from '../../styles';
4
+
5
+ export const commandList = style({
6
+ width: '100%',
7
+ maxHeight: 350,
8
+ overflow: 'auto',
9
+ paddingInline: sys.spacing[2],
10
+ paddingBlockEnd: sys.spacing[2],
11
+
12
+ overscrollBehavior: 'contain',
13
+ transition: transition(['height']),
14
+ });
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+
3
+ import { clsx } from 'clsx';
4
+ import { Command as CommandPrimitive } from 'cmdk';
5
+ import { ComponentPropsWithRef } from 'react';
6
+
7
+ import { commandList } from './command-list.css';
8
+
9
+ export function CommandList({
10
+ className,
11
+ ...props
12
+ }: ComponentPropsWithRef<typeof CommandPrimitive.List>) {
13
+ return (
14
+ <CommandPrimitive.List
15
+ className={clsx(commandList, className)}
16
+ {...props}
17
+ />
18
+ );
19
+ }
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+
3
+ import { clsx } from 'clsx';
4
+ import { Command as CommandPrimitive } from 'cmdk';
5
+ import { ComponentPropsWithRef } from 'react';
6
+
7
+ import {
8
+ MenuSeparatorVariants,
9
+ menuSeparator,
10
+ } from '../menu/menu-separator.css';
11
+
12
+ export type CommandSeparatorProps = ComponentPropsWithRef<
13
+ typeof CommandPrimitive.Separator
14
+ > &
15
+ MenuSeparatorVariants;
16
+
17
+ export function CommandSeparator({
18
+ className,
19
+ spacing = 4,
20
+ offset = true,
21
+ ...props
22
+ }: CommandSeparatorProps) {
23
+ return (
24
+ <CommandPrimitive.Separator
25
+ className={clsx(menuSeparator({ offset, spacing }), className)}
26
+ {...props}
27
+ />
28
+ );
29
+ }
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+
3
+ import { clsx } from 'clsx';
4
+ import { Command as CommandPrimitive } from 'cmdk';
5
+ import { ComponentPropsWithRef } from 'react';
6
+
7
+ import { Stack, StackVariants } from '../stack';
8
+
9
+ export type CommandProps = ComponentPropsWithRef<typeof CommandPrimitive> & {
10
+ spacing?: StackVariants['spacing'];
11
+ };
12
+
13
+ export function Command({
14
+ className,
15
+ children,
16
+ spacing = 2,
17
+ ...props
18
+ }: CommandProps) {
19
+ return (
20
+ <CommandPrimitive className={clsx(className)} {...props}>
21
+ <Stack spacing={spacing}>{children}</Stack>
22
+ </CommandPrimitive>
23
+ );
24
+ }
@@ -0,0 +1,7 @@
1
+ export { Command } from './command';
2
+ export { CommandInput } from './command-input';
3
+ export { CommandList } from './command-list';
4
+ export { CommandEmpty } from './command-empty';
5
+ export { CommandGroup } from './command-group';
6
+ export { CommandItem } from './command-item';
7
+ export { CommandSeparator } from './command-separator';
@@ -1,2 +1,2 @@
1
1
  export { Cover } from './cover';
2
- export { coverRecipe, type CoverVariants } from './cover.css';
2
+ export { coverRecipe, minSizeVar, type CoverVariants } from './cover.css';
@@ -0,0 +1,11 @@
1
+ import { style } from '@vanilla-extract/css';
2
+
3
+ import { sys } from '../../styles';
4
+
5
+ export const divider = style({
6
+ height: 1,
7
+ width: '100%',
8
+
9
+ border: 'none',
10
+ backgroundColor: sys.color.foreground,
11
+ });
@@ -0,0 +1,11 @@
1
+ import { clsx } from 'clsx';
2
+
3
+ import { divider } from './divider.css';
4
+
5
+ export interface DividerProps {
6
+ className?: string;
7
+ }
8
+
9
+ export function Divider({ className }: DividerProps) {
10
+ return <hr className={clsx(divider, className)} />;
11
+ }
@@ -0,0 +1 @@
1
+ export { Divider, type DividerProps } from './divider';
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+
3
+ import { createRequiredContext } from '@kalink-ui/dibbly';
4
+
5
+ interface FormFieldContext {
6
+ name: string;
7
+ registerMessageId: (id: string) => void;
8
+ unRegisterMessageId: (id: string) => void;
9
+ messageIds: string[];
10
+ errors?: string | null;
11
+ hideErrorMessage: boolean;
12
+ label: string;
13
+ disabled?: boolean;
14
+ hideLabel: boolean;
15
+ }
16
+
17
+ export const [useFormFieldContext, FormFieldContextProvider] =
18
+ createRequiredContext<FormFieldContext>();
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ import {
4
+ cloneElement,
5
+ HTMLAttributes,
6
+ isValidElement,
7
+ ReactElement,
8
+ } from 'react';
9
+
10
+ import { useFormFieldContext } from './form-field-context';
11
+ import { useFormFieldItemContext } from './form-field-item-context';
12
+
13
+ export interface FormFieldControlProps extends HTMLAttributes<HTMLElement> {
14
+ children: ReactElement;
15
+ }
16
+
17
+ export function FormFieldControl({
18
+ children,
19
+ ...props
20
+ }: FormFieldControlProps) {
21
+ const { messageIds, errors } = useFormFieldContext();
22
+ const { id } = useFormFieldItemContext();
23
+
24
+ if (!isValidElement(children)) {
25
+ throw new Error('FormFieldControl must have a valid child');
26
+ }
27
+
28
+ return cloneElement(children, {
29
+ id,
30
+ 'aria-describedby': messageIds.join(' ') || undefined,
31
+ 'aria-invalid': !!errors || undefined,
32
+ ...props,
33
+ });
34
+ }
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+
3
+ import { useFormFieldItemContext } from './form-field-item-context';
4
+ import { FormFieldMessage, FormFieldMessageProps } from './form-field-message';
5
+
6
+ export type FormFieldDescriptionProps = Omit<FormFieldMessageProps, 'id'>;
7
+
8
+ export function FormFieldDescription(props: FormFieldDescriptionProps) {
9
+ const { id } = useFormFieldItemContext();
10
+
11
+ if (!props.children) {
12
+ return;
13
+ }
14
+
15
+ return <FormFieldMessage id={`${id}-description`} {...props} />;
16
+ }
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+
3
+ import { useFormFieldContext } from './form-field-context';
4
+ import { useFormFieldItemContext } from './form-field-item-context';
5
+ import { FormFieldMessage, FormFieldMessageProps } from './form-field-message';
6
+
7
+ export type FormFieldErrorProps = Omit<FormFieldMessageProps, 'id'>;
8
+
9
+ export function FormFieldError(props: FormFieldErrorProps) {
10
+ const { errors, hideErrorMessage } = useFormFieldContext();
11
+ const { id } = useFormFieldItemContext();
12
+
13
+ if (!errors || hideErrorMessage) {
14
+ return null;
15
+ }
16
+
17
+ return (
18
+ <FormFieldMessage id={`${id}-error`} error {...props}>
19
+ {errors}
20
+ </FormFieldMessage>
21
+ );
22
+ }
@@ -0,0 +1,6 @@
1
+ 'use client';
2
+
3
+ import { createRequiredContext } from '@kalink-ui/dibbly';
4
+
5
+ export const [useFormFieldItemContext, FormFieldItemContextProvider] =
6
+ createRequiredContext<{ id: string }>();
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+
3
+ import { PolymorphicComponentProps } from '@kalink-ui/dibbly';
4
+ import { ElementType, useId } from 'react';
5
+
6
+ import { useFormFieldContext } from './form-field-context';
7
+ import { FormFieldItemContextProvider } from './form-field-item-context';
8
+ import { formFieldStyle } from './form-field.css';
9
+
10
+ export type FormFieldItemProps<TUse extends ElementType = 'div'> =
11
+ PolymorphicComponentProps<TUse>;
12
+
13
+ export function FormFieldItem<TUse extends ElementType = 'div'>(
14
+ props: FormFieldItemProps<TUse>,
15
+ ) {
16
+ const id = useId();
17
+ const { errors, disabled } = useFormFieldContext();
18
+
19
+ const { use: Comp = 'div', className, children, ...rest } = props;
20
+
21
+ return (
22
+ <FormFieldItemContextProvider value={{ id }}>
23
+ <Comp className={formFieldStyle({ error: !!errors, disabled })} {...rest}>
24
+ {children}
25
+ </Comp>
26
+ </FormFieldItemContextProvider>
27
+ );
28
+ }
@@ -0,0 +1,27 @@
1
+ import { clsx } from 'clsx';
2
+
3
+ import { visuallyHidden } from '../../styles';
4
+ import { Label, LabelProps } from '../label';
5
+
6
+ import { useFormFieldContext } from './form-field-context';
7
+ import { useFormFieldItemContext } from './form-field-item-context';
8
+
9
+ export function FormFieldLabel({
10
+ className,
11
+ children,
12
+ required,
13
+ ...props
14
+ }: LabelProps) {
15
+ const { errors, hideLabel } = useFormFieldContext();
16
+ const { id } = useFormFieldItemContext();
17
+
18
+ return (
19
+ <Label
20
+ className={clsx(visuallyHidden({ hidden: hideLabel }), className)}
21
+ htmlFor={id}
22
+ error={!!errors}
23
+ required={required}
24
+ {...props}
25
+ >{`${children}${required ? ' *' : ''}`}</Label>
26
+ );
27
+ }
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { HTMLAttributes, useEffect } from 'react';
4
+
5
+ import { useFormFieldContext } from './form-field-context';
6
+ import { formFieldMessageStyle } from './form-field.css';
7
+
8
+ export type FormFieldMessageProps = HTMLAttributes<HTMLDivElement> & {
9
+ id: string;
10
+ error?: boolean;
11
+ };
12
+
13
+ export function FormFieldMessage({
14
+ className,
15
+ children,
16
+ id,
17
+ error,
18
+ ...props
19
+ }: FormFieldMessageProps) {
20
+ const { registerMessageId, unRegisterMessageId } = useFormFieldContext();
21
+
22
+ useEffect(() => {
23
+ registerMessageId(id);
24
+
25
+ return () => unRegisterMessageId(id);
26
+ }, [id, registerMessageId, unRegisterMessageId]);
27
+
28
+ return (
29
+ <div id={id} className={formFieldMessageStyle} {...props}>
30
+ {children}
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,97 @@
1
+ import { createGlobalTheme, style } from '@vanilla-extract/css';
2
+ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
3
+
4
+ import { sys, typography } from '../../styles';
5
+ import { components } from '../../styles/layers.css';
6
+
7
+ export const formFieldVars = createGlobalTheme(':root', {
8
+ '@layer': components,
9
+
10
+ spacing: {
11
+ vertical: sys.spacing[2],
12
+ },
13
+
14
+ color: {
15
+ foreground: sys.color.foreground,
16
+ background: sys.color.background,
17
+ outline: sys.color.foreground,
18
+ },
19
+ });
20
+
21
+ export const formFieldStyle = recipe({
22
+ base: {
23
+ '@layer': {
24
+ [components]: {
25
+ display: 'flex',
26
+ flexDirection: 'column',
27
+ gap: formFieldVars.spacing.vertical,
28
+
29
+ width: '100%',
30
+ maxWidth: '100%',
31
+
32
+ color: formFieldVars.color.foreground,
33
+
34
+ selectors: {
35
+ '&:disabled, &:has(:disabled)': {
36
+ cursor: 'not-allowed',
37
+
38
+ vars: {
39
+ [formFieldVars.color.foreground]:
40
+ `color(from ${sys.color.foreground} srgb r g b / 0.38)`,
41
+ },
42
+ },
43
+
44
+ '&[aria-invalid], &:has([aria-invalid])': {
45
+ vars: {
46
+ [formFieldVars.color.foreground]: 'red',
47
+ },
48
+ },
49
+ },
50
+ },
51
+ },
52
+ },
53
+
54
+ variants: {
55
+ error: {
56
+ true: {
57
+ '@layer': {
58
+ [components]: {
59
+ vars: {
60
+ [formFieldVars.color.foreground]: 'red',
61
+ },
62
+ },
63
+ },
64
+ },
65
+ },
66
+
67
+ disabled: {
68
+ true: {
69
+ '@layer': {
70
+ [components]: {
71
+ cursor: 'not-allowed',
72
+
73
+ vars: {
74
+ [formFieldVars.color.foreground]:
75
+ `color(from ${sys.color.foreground} srgb r g b / ${sys.state.muted.light})`,
76
+ },
77
+ },
78
+ },
79
+ },
80
+ },
81
+ },
82
+ });
83
+
84
+ export const formFieldMessageStyle = style([
85
+ typography.body.small,
86
+ {
87
+ '@layer': {
88
+ [components]: {
89
+ display: 'block',
90
+ },
91
+ },
92
+ },
93
+ ]);
94
+
95
+ export type FormFieldVariants = NonNullable<
96
+ RecipeVariants<typeof formFieldStyle>
97
+ >;
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+
3
+ import { useState, useCallback, ReactNode } from 'react';
4
+
5
+ import { FormFieldContextProvider } from './form-field-context';
6
+
7
+ export interface FormFieldProps {
8
+ name: string;
9
+ label: string;
10
+ children: ReactNode;
11
+ errors?: string | null;
12
+ hideErrorMessage: boolean;
13
+ hideLabel: boolean;
14
+ disabled?: boolean;
15
+ }
16
+
17
+ export function FormField({
18
+ name,
19
+ label,
20
+ children,
21
+ errors,
22
+ hideErrorMessage = false,
23
+ hideLabel = false,
24
+ disabled,
25
+ }: FormFieldProps) {
26
+ const [messageIds, setMessageIds] = useState<string[]>([]);
27
+
28
+ const registerMessageId = useCallback(
29
+ (id: string) => setMessageIds((ids) => [...new Set([...ids, id])]),
30
+ [setMessageIds],
31
+ );
32
+
33
+ const unRegisterMessageId = useCallback(
34
+ (id: string) =>
35
+ setMessageIds((ids) => ids.filter((current) => current !== id)),
36
+ [setMessageIds],
37
+ );
38
+
39
+ return (
40
+ <FormFieldContextProvider
41
+ value={{
42
+ name,
43
+ registerMessageId,
44
+ unRegisterMessageId,
45
+ messageIds,
46
+ errors,
47
+ hideErrorMessage,
48
+ label,
49
+ disabled,
50
+ hideLabel,
51
+ }}
52
+ >
53
+ {children}
54
+ </FormFieldContextProvider>
55
+ );
56
+ }
@@ -0,0 +1,9 @@
1
+ export { useFormFieldContext } from './form-field-context';
2
+ export { useFormFieldItemContext } from './form-field-item-context';
3
+ export { formFieldStyle } from './form-field.css';
4
+ export { FormFieldControl } from './form-field-control';
5
+ export { FormFieldDescription } from './form-field-description';
6
+ export { FormFieldError } from './form-field-error';
7
+ export { FormField } from './form-field';
8
+ export { FormFieldItem } from './form-field-item';
9
+ export { FormFieldLabel } from './form-field-label';