@maestro-js/components 1.0.0-alpha.19 → 1.0.0-alpha.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 (56) hide show
  1. package/commands/add.ts +1 -16
  2. package/dist/components.json +1 -1
  3. package/dist/index.js +1 -14
  4. package/package.json +5 -5
  5. package/registry.json +790 -598
  6. package/scripts/build.ts +4 -11
  7. package/src/components/alert-dialog.tsx +3 -3
  8. package/src/components/avatar.tsx +1 -1
  9. package/src/components/button-link.tsx +0 -3
  10. package/src/components/button.tsx +0 -4
  11. package/src/components/checkbox.tsx +7 -7
  12. package/src/components/chip.tsx +7 -7
  13. package/src/components/container.tsx +3 -3
  14. package/src/components/dialog.tsx +7 -21
  15. package/src/components/disclosure.tsx +1 -1
  16. package/src/components/drawer.tsx +5 -19
  17. package/src/components/form.tsx +5 -27
  18. package/src/components/helpers/form-field.tsx +8 -7
  19. package/src/components/helpers/get-button-classes.ts +23 -23
  20. package/src/components/helpers/headless-button.tsx +1 -1
  21. package/src/components/inline-alert.tsx +3 -3
  22. package/src/components/labeled-value.tsx +2 -2
  23. package/src/components/menu.tsx +26 -60
  24. package/src/components/month-day-input.tsx +1 -2
  25. package/src/components/multiselect.tsx +70 -74
  26. package/src/components/radio.tsx +8 -8
  27. package/src/components/select.tsx +8 -9
  28. package/src/components/stepper.tsx +6 -6
  29. package/src/components/switch.tsx +7 -7
  30. package/src/components/tabs.tsx +1 -1
  31. package/src/components/tag-field.tsx +3 -3
  32. package/src/components/text-area.tsx +1 -1
  33. package/src/components/text-field.tsx +1 -4
  34. package/src/components/toast.tsx +6 -6
  35. package/src/components/toggle-button-group.tsx +2 -2
  36. package/src/utils/icons.d.ts +1 -2
  37. package/src/utils/use-tab-indicator.ts +9 -9
  38. package/src/components/date-range.tsx +0 -138
  39. package/src/components/optional-link.tsx +0 -15
  40. package/src/components/table/headless-templated-row-table.ts +0 -550
  41. package/src/components/table/index.tsx +0 -41
  42. package/src/components/table/server-table.ts +0 -143
  43. package/src/components/table/table-actions.tsx +0 -76
  44. package/src/components/table/table-container.tsx +0 -173
  45. package/src/components/table/table-container.type-test.ts +0 -155
  46. package/src/components/table/table-filters/date-range-filter.tsx +0 -227
  47. package/src/components/table/table-filters/select-filter.tsx +0 -211
  48. package/src/components/table/table-filters/sort.tsx +0 -85
  49. package/src/components/table/table-primitives.tsx +0 -226
  50. package/src/components/table/table-toolbar.tsx +0 -300
  51. package/src/components/table/table-types.ts +0 -61
  52. package/src/components/table/use-client-table.ts +0 -157
  53. package/src/components/table/use-client-table.type-test.ts +0 -217
  54. package/src/components/table/use-server-table.ts +0 -192
  55. package/src/components/table/use-server-table.type-test.ts +0 -174
  56. package/src/utils/use-query-state.ts +0 -626
package/scripts/build.ts CHANGED
@@ -9,7 +9,6 @@ interface RegistryFile {
9
9
 
10
10
  interface RegistryEntry {
11
11
  name: string
12
- dependencies?: string[]
13
12
  files: RegistryFile[]
14
13
  }
15
14
 
@@ -20,7 +19,6 @@ interface OutputFile {
20
19
 
21
20
  interface OutputEntry {
22
21
  name: string
23
- dependencies?: string[]
24
22
  files: OutputFile[]
25
23
  }
26
24
 
@@ -31,19 +29,14 @@ const registry: RegistryEntry[] = JSON.parse(await readFile(resolve(packageRoot,
31
29
  const output: OutputEntry[] = await Promise.all(
32
30
  registry.map(async (entry) => {
33
31
  const files = await Promise.all(
34
- entry.files.map(async (file) => {
35
- const content = await readFile(resolve(packageRoot, file.path), 'utf-8')
36
- const header = `// From @maestro-js/components\n// Install with: pnpm maestro components:add ${entry.name} --directory app\n\n`
37
- return {
38
- name: `${file.type}/${basename(file.path)}`,
39
- content: header + content
40
- }
41
- })
32
+ entry.files.map(async (file) => ({
33
+ name: `${file.type}/${basename(file.path)}`,
34
+ content: await readFile(resolve(packageRoot, file.path), 'utf-8')
35
+ }))
42
36
  )
43
37
 
44
38
  return {
45
39
  name: entry.name,
46
- ...(entry.dependencies?.length && { dependencies: entry.dependencies }),
47
40
  files
48
41
  }
49
42
  })
@@ -86,7 +86,7 @@ function AlertDialog_({
86
86
  isDismissable={isDismissable}
87
87
  isKeyboardDismissDisabled={!isDismissable}
88
88
  className={tw(
89
- 'fixed inset-0 z-[4000] flex items-center justify-center bg-black/25 backdrop-blur-sm',
89
+ 'fixed inset-0 z-[4000] flex items-center justify-center bg-black/70 backdrop-blur-lg',
90
90
  'transition-opacity duration-200 ease-out',
91
91
  'data-[entering]:opacity-0',
92
92
  'data-[exiting]:opacity-0'
@@ -100,14 +100,14 @@ function AlertDialog_({
100
100
  'data-[exiting]:scale-95 data-[exiting]:opacity-0 data-[exiting]:translate-y-2'
101
101
  )}
102
102
  >
103
- <Dialog className={tw('outline-none bg-white rounded-xl shadow-xl ring-1 ring-black/5 w-full', className)} role="alertdialog">
103
+ <Dialog className={tw('outline-none bg-white rounded-lg shadow-2xl w-full', className)} role="alertdialog">
104
104
  {({ close }) => (
105
105
  <AlertDialogContext.Provider value={{ close }}>
106
106
  <ButtonGroupContext.Provider
107
107
  value={{ orientation: 'horizontal', alignment: 'center', fullWidth: true, className: 'flex-nowrap' }}
108
108
  >
109
109
  <ButtonContext.Provider value={{ shape: 'rectangle', variant: 'soft', fullWidth: true }}>
110
- <div className={tw('border rounded-xl p-6', styles.container)}>
110
+ <div className={tw('border rounded-lg p-6', styles.container)}>
111
111
  <div className="flex flex-col items-center text-center">
112
112
  {displayIcon && (
113
113
  <div className={tw('w-12 h-12 rounded-xl flex items-center justify-center mb-4', styles.iconBg)}>
@@ -22,7 +22,7 @@ export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
22
22
  <div
23
23
  ref={ref}
24
24
  className={tw(
25
- 'size-10 relative inline-flex items-center justify-center overflow-hidden font-medium text-white select-none ring-1 ring-black/5',
25
+ 'size-10 relative inline-flex items-center justify-center overflow-hidden font-medium text-white select-none',
26
26
  !showImage && 'bg-neutral-300',
27
27
  shape === 'circle' && 'rounded-full',
28
28
  shape === 'rounded' && 'rounded',
@@ -56,9 +56,6 @@ export const ButtonLink = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(
56
56
  className: tw(fullWidth && 'w-full', className)
57
57
  })}
58
58
  >
59
- {(size === 'xs' || size === 'sm' || shape === 'circle') && (
60
- <span className="absolute left-1/2 top-1/2 size-12 -translate-x-1/2 -translate-y-1/2" aria-hidden="true" />
61
- )}
62
59
  <span className={tw('inline-flex items-center gap-2', showSpinner && 'opacity-0')}>{children}</span>
63
60
  <span
64
61
  aria-hidden={!showSpinner ? true : undefined}
@@ -74,7 +74,6 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
74
74
  ref={ref}
75
75
  data-pending={isPending}
76
76
  onClick={onClick}
77
- {...(isToggled != null ? { 'aria-pressed': isToggled } : {})}
78
77
  className={composeTailwindRenderProps(
79
78
  props.className,
80
79
  tw(getButtonClasses({ variant, color, size, shape, isToggled }), fullWidth && 'w-full')
@@ -82,9 +81,6 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
82
81
  >
83
82
  {composeRenderProps(props.children, (children) => (
84
83
  <>
85
- {(size === 'xs' || size === 'sm' || shape === 'circle') && (
86
- <span className="absolute left-1/2 top-1/2 size-12 -translate-x-1/2 -translate-y-1/2" aria-hidden="true" />
87
- )}
88
84
  <span className={tw('inline-flex items-center gap-2', showSpinner && 'opacity-0')}>{children}</span>
89
85
  <span
90
86
  aria-hidden={!showSpinner ? true : undefined}
@@ -78,7 +78,7 @@ export function PlainCheckbox(props: PlainCheckboxProps) {
78
78
  isDisabled={props.isDisabled}
79
79
  isInvalid={props.isInvalid || validationInvalid}
80
80
  aria-label={props['aria-label']}
81
- className="group inline-flex items-center focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600"
81
+ className="group inline-flex items-center focus:outline-none [&>div]:focus-visible:ring-2 [&>div]:focus-visible:ring-primary-500/20 [&>div]:focus-visible:ring-offset-2"
82
82
  >
83
83
  {({ isSelected, isIndeterminate, isDisabled }) => (
84
84
  <>
@@ -139,7 +139,7 @@ export function LabeledCheckbox(props: LabeledCheckboxProps) {
139
139
  isDisabled={props.isDisabled}
140
140
  isInvalid={isInvalid}
141
141
  aria-describedby={helpTextId}
142
- className="group relative flex items-start gap-3 select-none focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600"
142
+ className="group relative flex items-start gap-3 select-none focus:outline-none [&>div:first-child>div]:focus-visible:ring-2 [&>div:first-child>div]:focus-visible:ring-primary-500/20 [&>div:first-child>div]:focus-visible:ring-offset-2"
143
143
  >
144
144
  {({ isSelected, isIndeterminate, isDisabled }) => (
145
145
  <>
@@ -191,16 +191,16 @@ function CheckboxBox({
191
191
  return (
192
192
  <div
193
193
  className={tw(
194
- 'relative size-5 rounded border transition-colors duration-200',
195
- isSelected || isIndeterminate ? 'bg-primary-600 border-primary-600' : 'bg-white border-neutral-300',
194
+ 'relative h-5 w-5 rounded border transition-all duration-50',
195
+ isSelected || isIndeterminate ? 'bg-primary-500 border-primary-500' : 'bg-white border-neutral-300',
196
196
  !isDisabled && !(isSelected || isIndeterminate) && 'group-hovered:border-neutral-400',
197
- isDisabled && 'cursor-not-allowed border-neutral-300 bg-neutral-100'
197
+ isDisabled && 'opacity-50 cursor-not-allowed'
198
198
  )}
199
199
  >
200
200
  {isIndeterminate ? (
201
- <Icon name="minus" className="h-5/6 absolute inset-0 m-auto aspect-square stroke-white text-white" />
201
+ <Icon name="minus" className="h-5/6 absolute inset-0 m-auto aspect-square text-white" />
202
202
  ) : (
203
- <Icon name="check" className="h-5/6 absolute inset-0 m-auto aspect-square stroke-white text-white" />
203
+ <Icon name="check" className="h-5/6 absolute inset-0 m-auto aspect-square text-white" />
204
204
  )}
205
205
  </div>
206
206
  )
@@ -60,7 +60,7 @@ export function Chip(props: ChipProps) {
60
60
  className={tw(
61
61
  'inline-flex items-center justify-center rounded-full transition-colors translate-x-1',
62
62
  removeBtnSizeClasses[size],
63
- !isDisabled && 'hover:bg-neutral-950/10',
63
+ !isDisabled && 'hover:bg-black/10',
64
64
  isDisabled && 'cursor-not-allowed'
65
65
  )}
66
66
  aria-label="Remove"
@@ -146,13 +146,13 @@ const darkHoverColorClasses = {
146
146
  }
147
147
 
148
148
  const removeBtnSizeClasses = {
149
- sm: 'size-4 -m-0.5 p-0.5',
150
- md: 'size-5 -m-1 p-1',
151
- lg: 'size-6 -m-1.5 p-1.5'
149
+ sm: 'h-4 w-4 -m-0.5 p-0.5',
150
+ md: 'h-5 w-5 -m-1 p-1',
151
+ lg: 'h-6 w-6 -m-1.5 p-1.5'
152
152
  }
153
153
 
154
154
  const removeIconSizeClasses = {
155
- sm: 'size-2',
156
- md: 'size-3',
157
- lg: 'size-4'
155
+ sm: 'h-2 w-2',
156
+ md: 'h-3 w-3',
157
+ lg: 'h-4 w-4'
158
158
  }
@@ -71,9 +71,9 @@ const sizeClasses: Record<ContainerSize, string> = {
71
71
  }
72
72
 
73
73
  const elevationClasses: Record<string, string> = {
74
- '0': 'bg-white ring-1 ring-neutral-950/5',
75
- '-1': 'bg-neutral-50 ring-1 ring-neutral-950/5',
76
- '-2': 'bg-neutral-100 ring-1 ring-neutral-950/10',
74
+ '0': 'bg-white border border-neutral-200',
75
+ '-1': 'bg-neutral-50 border border-neutral-200',
76
+ '-2': 'bg-neutral-100 border border-neutral-300',
77
77
  inherit: ''
78
78
  }
79
79
 
@@ -1,6 +1,4 @@
1
1
  import React from 'react'
2
- import type { To, RelativeRoutingType } from 'react-router'
3
- import { useNavigate } from 'react-router'
4
2
  import { Modal, ModalOverlay, Dialog as AriaDialog, Heading } from 'react-aria-components'
5
3
  import { tw } from '../utils/tw'
6
4
  import { Button } from './button'
@@ -9,10 +7,8 @@ export type DialogProps = {
9
7
  children?: React.ReactNode
10
8
  title?: React.ReactNode
11
9
  isOpen?: boolean
12
- close?:
13
- | ((isOpen: boolean) => void)
14
- | { to: To; replace?: boolean; relative?: RelativeRoutingType; reloadDocument?: boolean }
15
- size?: 'xs' | 'sm' | 'lg'
10
+ onOpenChange?: (isOpen: boolean) => void
11
+ size?: 'sm' | 'lg'
16
12
  className?: string
17
13
  isDismissable?: boolean
18
14
  actions?: React.ReactNode
@@ -28,22 +24,12 @@ function Dialog_({
28
24
  children,
29
25
  title,
30
26
  isOpen = false,
31
- close,
27
+ onOpenChange,
32
28
  size = 'sm',
33
29
  className,
34
30
  isDismissable = false,
35
31
  actions
36
32
  }: DialogProps) {
37
- const navigate = useNavigate()
38
-
39
- const onOpenChange = close
40
- ? typeof close === 'function'
41
- ? close
42
- : (open: boolean) => {
43
- if (!open) navigate(close.to, { replace: close.replace, relative: close.relative })
44
- }
45
- : undefined
46
-
47
33
  return (
48
34
  <ModalOverlay
49
35
  isOpen={isOpen}
@@ -51,7 +37,7 @@ function Dialog_({
51
37
  isDismissable={isDismissable}
52
38
  isKeyboardDismissDisabled={!isDismissable}
53
39
  className={tw(
54
- 'fixed inset-0 z-[2000] flex items-center justify-center bg-black/25 backdrop-blur-sm',
40
+ 'fixed inset-0 z-[2000] flex items-center justify-center bg-black/70 backdrop-blur-lg',
55
41
  'transition-opacity duration-300 ease-in-out',
56
42
  'data-[entering]:opacity-0',
57
43
  'data-[exiting]:opacity-0'
@@ -60,7 +46,7 @@ function Dialog_({
60
46
  <Modal
61
47
  className={tw(
62
48
  'w-[calc(100%-4rem)] m-auto',
63
- size === 'lg' ? 'max-w-[1080px]' : size === 'xs' ? 'max-w-[400px]' : 'max-w-[640px]',
49
+ size === 'lg' ? 'max-w-[1080px]' : 'max-w-[640px]',
64
50
  'transition-all duration-300 ease-in-out',
65
51
  'data-[entering]:scale-95 data-[entering]:opacity-0',
66
52
  'data-[exiting]:scale-95 data-[exiting]:opacity-0'
@@ -68,7 +54,7 @@ function Dialog_({
68
54
  >
69
55
  <AriaDialog
70
56
  className={tw(
71
- 'w-full max-h-[calc(100vh-6rem)] bg-white shadow-xl ring-1 ring-black/5 outline-none rounded-xl',
57
+ 'w-full max-h-[calc(100vh-6rem)] bg-white shadow-xl outline-none rounded-lg',
72
58
  'flex flex-col overflow-x-hidden overflow-y-auto',
73
59
  className
74
60
  )}
@@ -77,7 +63,7 @@ function Dialog_({
77
63
  <DialogContext.Provider value={{ close }}>
78
64
  <div className="flex items-start justify-between gap-4 pt-6 px-6">
79
65
  {title ? (
80
- <Heading slot="title" className="flex-1 text-lg font-semibold text-neutral-900">
66
+ <Heading slot="title" className="flex-1 text-xl font-semibold text-neutral-900">
81
67
  {title}
82
68
  </Heading>
83
69
  ) : (
@@ -85,7 +85,7 @@ function DisclosureHeader({
85
85
  name="chevron-right"
86
86
  className={tw(
87
87
  'shrink-0 transition-transform duration-200 ease-in-out',
88
- size === 'md' || size === 'lg' ? 'size-4' : 'size-3',
88
+ size === 'md' || size === 'lg' ? 'w-4 h-4' : 'w-3 h-3',
89
89
  isExpanded && 'rotate-90',
90
90
  isDisabled ? 'text-neutral-300' : 'text-neutral-500'
91
91
  )}
@@ -1,6 +1,4 @@
1
1
  import React from 'react'
2
- import type { To, RelativeRoutingType } from 'react-router'
3
- import { useNavigate } from 'react-router'
4
2
  import { Drawer as VaulDrawer } from 'vaul'
5
3
  import { tw } from '../utils/tw'
6
4
 
@@ -8,9 +6,7 @@ export type DrawerProps = {
8
6
  children?: React.ReactNode
9
7
  title?: React.ReactNode
10
8
  isOpen?: boolean
11
- close?:
12
- | ((isOpen: boolean) => void)
13
- | { to: To; replace?: boolean; relative?: RelativeRoutingType; reloadDocument?: boolean }
9
+ onOpenChange?: (isOpen: boolean) => void
14
10
  size?: 'sm' | 'md' | 'lg'
15
11
  className?: string
16
12
  isDismissable?: boolean
@@ -23,7 +19,7 @@ export function Drawer({
23
19
  children,
24
20
  title,
25
21
  isOpen = false,
26
- close,
22
+ onOpenChange,
27
23
  size = 'md',
28
24
  className,
29
25
  isDismissable = true,
@@ -31,20 +27,10 @@ export function Drawer({
31
27
  actions,
32
28
  contentsContainerClassName
33
29
  }: DrawerProps) {
34
- const navigate = useNavigate()
35
-
36
- const onOpenChange = close
37
- ? typeof close === 'function'
38
- ? close
39
- : (open: boolean) => {
40
- if (!open) navigate(close.to, { replace: close.replace, relative: close.relative })
41
- }
42
- : undefined
43
-
44
30
  return (
45
31
  <VaulDrawer.Root open={isOpen} onOpenChange={onOpenChange} direction={position} dismissible={isDismissable}>
46
32
  <VaulDrawer.Portal>
47
- <VaulDrawer.Overlay className="fixed inset-0 z-49 bg-black/25 backdrop-blur-sm" />
33
+ <VaulDrawer.Overlay className="fixed inset-0 z-49 bg-black/70 backdrop-blur-lg" />
48
34
  <VaulDrawer.Content
49
35
  className={tw(
50
36
  'flex h-auto flex-col text-sm group/drawer-content fixed z-50',
@@ -64,7 +50,7 @@ export function Drawer({
64
50
  {isDismissable ? (
65
51
  <div
66
52
  className={tw(
67
- 'absolute bg-neutral-300 rounded-full shrink-0 group-data-[vaul-drawer-direction=bottom]/drawer-content:block',
53
+ 'absolute bg-neutral-200 rounded-full shrink-0 group-data-[vaul-drawer-direction=bottom]/drawer-content:block',
68
54
  'group-data-[vaul-drawer-direction=left]/drawer-content:h-25 group-data-[vaul-drawer-direction=left]/drawer-content:w-1 group-data-[vaul-drawer-direction=left]/drawer-content:right-2 group-data-[vaul-drawer-direction=left]/drawer-content:top-[calc(50%-50px)]',
69
55
  'group-data-[vaul-drawer-direction=right]/drawer-content:h-25 group-data-[vaul-drawer-direction=right]/drawer-content:w-1 group-data-[vaul-drawer-direction=right]/drawer-content:left-2 group-data-[vaul-drawer-direction=right]/drawer-content:top-[calc(50%-50px)]',
70
56
  'group-data-[vaul-drawer-direction=top]/drawer-content:w-25 group-data-[vaul-drawer-direction=top]/drawer-content:h-1 group-data-[vaul-drawer-direction=top]/drawer-content:bottom-2 group-data-[vaul-drawer-direction=top]/drawer-content:left-[calc(50%-50px)]',
@@ -73,7 +59,7 @@ export function Drawer({
73
59
  />
74
60
  ) : null}
75
61
 
76
- <div className="bg-white size-full pt-6 px-6 pb-6 rounded-xl shadow-xl ring-1 ring-black/5">
62
+ <div className="bg-white size-full pt-6 px-6 pb-6 rounded-md shadow-xl">
77
63
  <div className="flex items-start justify-between gap-4 pb-5">
78
64
  {title ? (
79
65
  <VaulDrawer.Title className="flex-1 text-xl font-semibold text-neutral-900">{title}</VaulDrawer.Title>
@@ -1,20 +1,19 @@
1
- import { forwardRef } from 'react'
2
1
  import {
3
2
  type FormProps as ReactRouterFormProps,
4
3
  Form as ReactRouterForm,
5
4
  data,
6
5
  type ClientActionFunctionArgs
7
6
  } from 'react-router'
8
- import { HeadlessForm, type StandardSchema, type ParseFormDataOptions, type ParseFormDataResult } from '@maestro-js/form'
7
+ import { HeadlessForm } from '@maestro-js/form'
9
8
  import { serverOnly$ } from 'vite-env-only/macros'
10
9
 
11
- const _Form = forwardRef<HTMLFormElement, ReactRouterFormProps>(function Form({ children, ...props }, ref) {
10
+ function _Form({ children, ...props }: ReactRouterFormProps) {
12
11
  return (
13
- <HeadlessForm as={ReactRouterForm} ref={ref} {...props}>
12
+ <HeadlessForm as={ReactRouterForm} {...props}>
14
13
  {children}
15
14
  </HeadlessForm>
16
15
  )
17
- })
16
+ }
18
17
 
19
18
  export const Form = Object.assign(_Form, {
20
19
  HiddenInput: HeadlessForm.HiddenInput,
@@ -24,28 +23,7 @@ export const Form = Object.assign(_Form, {
24
23
  useTopLevelFormValidation: HeadlessForm.useTopLevelFormValidation,
25
24
  useReset: HeadlessForm.useReset,
26
25
  clientSafeParse: HeadlessForm.parseFormDataClientSide,
27
- serverSafeParse<T>(
28
- schema: StandardSchema<T, T>,
29
- formData: FormData | Promise<FormData>,
30
- options?: {
31
- handleFile: ParseFormDataOptions<T>['handleFile']
32
- }
33
- ): Promise<ParseFormDataResult<T>> {
34
- return HeadlessForm.parseFormData(schema, formData, {
35
- ...options,
36
- jsonSchemaLibraryOptions: {
37
- unrepresentable: 'any',
38
- override: (ctx) => {
39
- const meta = ctx.zodSchema.meta()
40
- const custom = meta?.jsonSchema
41
-
42
- if (custom && typeof custom === 'object') {
43
- Object.assign(ctx.jsonSchema, custom)
44
- }
45
- }
46
- }
47
- })
48
- },
26
+ serverSafeParse: HeadlessForm.parseFormData,
49
27
  validationError: serverOnly$(validationError) as typeof validationError,
50
28
  customValidationError: serverOnly$(customValidationError) as typeof customValidationError,
51
29
  composeValidators: HeadlessForm.composeValidators,
@@ -85,16 +85,17 @@ const FieldGroup = React.forwardRef<HTMLDivElement, FieldGroupProps>(
85
85
  <Group
86
86
  ref={ref}
87
87
  className={tw(
88
- 'relative overflow-hidden ring-1 shadow-sm transition-all duration-200 max-w-96',
89
- 'focus-within:outline-2 focus-within:-outline-offset-1',
88
+ 'relative overflow-hidden border transition-all duration-200 max-w-96',
89
+ 'focus-within:ring-1',
90
90
  shape,
91
- disabled && 'ring-neutral-200 bg-neutral-50 text-neutral-400 shadow-none cursor-not-allowed',
92
- opts.isInvalid && 'ring-negative-300 bg-negative-50 focus-within:outline-negative-500',
93
- opts.hasWarning && 'ring-notice-300 bg-notice-50 focus-within:outline-notice-500',
91
+ disabled && 'border-neutral-100 bg-neutral-50 text-neutral-400 cursor-not-allowed',
92
+ opts.isInvalid && 'border-negative-200 bg-negative-50 focus-within:border-negative-500 focus-within:ring-negative-500/20',
93
+ opts.hasWarning &&
94
+ 'border-notice-200 bg-notice-50 focus-within:border-notice-500 focus-within:ring-notice-500/20',
94
95
  !opts.isInvalid &&
95
96
  !opts.hasWarning &&
96
97
  !disabled &&
97
- 'ring-black/10 hover:ring-black/20 focus-within:outline-primary-500',
98
+ 'border-neutral-200 hover:border-neutral-300 focus-within:border-primary-500 focus-within:ring-primary-500/20',
98
99
  className
99
100
  )}
100
101
  onClick={onClick}
@@ -113,7 +114,7 @@ const FieldInput = React.forwardRef<HTMLInputElement, InputProps>((props, ref) =
113
114
  className={composeTailwindRenderProps(
114
115
  props.className,
115
116
  tw(
116
- 'flex-1 bg-transparent px-3 py-2 border-0 focus:ring-0 focus:outline-none text-sm max-sm:text-base text-neutral-900 placeholder:text-neutral-400',
117
+ 'flex-1 bg-transparent px-3 py-2 border-0 focus:ring-0 focus:outline-none text-sm text-neutral-900 placeholder:text-neutral-400',
117
118
  (props.disabled || props.readOnly) && 'cursor-not-allowed'
118
119
  )
119
120
  )}
@@ -6,19 +6,19 @@ export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
6
6
  export type ButtonShape = 'rectangle' | 'circle' | 'oval'
7
7
 
8
8
  const base =
9
- 'relative inline-flex items-center justify-center gap-2 font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow,transform,opacity] duration-150 cursor-default select-none [&_svg]:pointer-events-none [&_svg:not([class*=size-])]:size-4 [&_svg]:shrink-0 data-[disabled]:cursor-not-allowed'
9
+ 'relative inline-flex items-center justify-center gap-2 font-medium whitespace-nowrap transition-all cursor-default select-none [&_svg]:pointer-events-none [&_svg:not([class*=size-])]:size-4 [&_svg]:shrink-0 data-[disabled]:cursor-not-allowed'
10
10
 
11
11
  const sizeClasses: Record<ButtonSize, string> = {
12
- xs: 'h-7 min-w-14 px-2 text-xs',
13
- sm: 'h-8 min-w-16 px-2.5 text-sm',
14
- md: 'h-9 min-w-20 px-3 text-sm',
15
- lg: 'h-10 min-w-24 px-4 text-base',
16
- xl: 'h-12 min-w-28 px-5 text-base'
12
+ xs: 'h-7 min-w-14 px-2.5 text-xs',
13
+ sm: 'h-8 min-w-16 px-3 text-sm',
14
+ md: 'h-9 min-w-20 px-4 text-sm',
15
+ lg: 'h-10 min-w-24 px-5 text-base',
16
+ xl: 'h-12 min-w-28 px-6 text-base'
17
17
  }
18
18
 
19
19
  const shapeClasses: Record<ButtonShape, string> = {
20
20
  rectangle: 'rounded',
21
- circle: 'rounded-full aspect-square px-0 min-w-0',
21
+ circle: 'rounded-full aspect-square px-0',
22
22
  oval: 'rounded-full'
23
23
  }
24
24
 
@@ -37,39 +37,39 @@ const variantColorClasses: Record<ButtonVariant, Record<ButtonColor, string>> =
37
37
  },
38
38
  outlined: {
39
39
  primary:
40
- 'bg-white ring-1 ring-inset ring-primary-300 text-primary-700 data-[hovered]:bg-primary-50 data-[hovered]:ring-primary-400 data-[pressed]:bg-primary-100 data-[pressed]:ring-primary-500 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400 data-[disabled]:ring-transparent',
40
+ 'bg-white ring-1 ring-inset ring-primary-300 text-primary-700 data-[hovered]:bg-primary-50 data-[hovered]:ring-primary-400 data-[pressed]:bg-primary-100 data-[pressed]:ring-primary-500 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400 data-[disabled]:ring-neutral-200 data-[disabled]:shadow-none',
41
41
  negative:
42
- 'bg-white ring-1 ring-inset ring-negative-300 text-negative-700 data-[hovered]:bg-negative-50 data-[hovered]:ring-negative-400 data-[pressed]:bg-negative-100 data-[pressed]:ring-negative-500 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400 data-[disabled]:ring-transparent',
42
+ 'bg-white ring-1 ring-inset ring-negative-300 text-negative-700 data-[hovered]:bg-negative-50 data-[hovered]:ring-negative-400 data-[pressed]:bg-negative-100 data-[pressed]:ring-negative-500 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400 data-[disabled]:ring-neutral-200 data-[disabled]:shadow-none',
43
43
  positive:
44
- 'bg-white ring-1 ring-inset ring-positive-300 text-positive-700 data-[hovered]:bg-positive-50 data-[hovered]:ring-positive-400 data-[pressed]:bg-positive-100 data-[pressed]:ring-positive-500 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400 data-[disabled]:ring-transparent',
44
+ 'bg-white ring-1 ring-inset ring-positive-300 text-positive-700 data-[hovered]:bg-positive-50 data-[hovered]:ring-positive-400 data-[pressed]:bg-positive-100 data-[pressed]:ring-positive-500 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400 data-[disabled]:ring-neutral-200 data-[disabled]:shadow-none',
45
45
  notice:
46
- 'bg-white ring-1 ring-inset ring-notice-300 text-notice-700 data-[hovered]:bg-notice-50 data-[hovered]:ring-notice-400 data-[pressed]:bg-notice-100 data-[pressed]:ring-notice-500 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400 data-[disabled]:ring-transparent',
46
+ 'bg-white ring-1 ring-inset ring-notice-300 text-notice-700 data-[hovered]:bg-notice-50 data-[hovered]:ring-notice-400 data-[pressed]:bg-notice-100 data-[pressed]:ring-notice-500 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400 data-[disabled]:ring-neutral-200 data-[disabled]:shadow-none',
47
47
  neutral:
48
- 'bg-white ring-1 ring-inset ring-neutral-300 text-neutral-700 data-[hovered]:bg-neutral-50 data-[hovered]:ring-neutral-400 data-[pressed]:bg-neutral-100 data-[pressed]:ring-neutral-500 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400 data-[disabled]:ring-transparent'
48
+ 'bg-white ring-1 ring-inset ring-neutral-300 text-neutral-700 data-[hovered]:bg-neutral-50 data-[hovered]:ring-neutral-400 data-[pressed]:bg-neutral-100 data-[pressed]:ring-neutral-500 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400 data-[disabled]:ring-neutral-200 data-[disabled]:shadow-none'
49
49
  },
50
50
  soft: {
51
51
  primary:
52
- 'bg-primary-100 text-primary-800 data-[hovered]:bg-primary-200 data-[pressed]:bg-primary-300 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400',
52
+ 'bg-primary-100 text-primary-800 data-[hovered]:bg-primary-200 data-[pressed]:bg-primary-300 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400',
53
53
  negative:
54
- 'bg-negative-100 text-negative-800 data-[hovered]:bg-negative-200 data-[pressed]:bg-negative-300 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400',
54
+ 'bg-negative-100 text-negative-800 data-[hovered]:bg-negative-200 data-[pressed]:bg-negative-300 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400',
55
55
  positive:
56
- 'bg-positive-100 text-positive-800 data-[hovered]:bg-positive-200 data-[pressed]:bg-positive-300 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400',
56
+ 'bg-positive-100 text-positive-800 data-[hovered]:bg-positive-200 data-[pressed]:bg-positive-300 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400',
57
57
  notice:
58
- 'bg-notice-100 text-notice-800 data-[hovered]:bg-notice-200 data-[pressed]:bg-notice-300 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400',
58
+ 'bg-notice-100 text-notice-800 data-[hovered]:bg-notice-200 data-[pressed]:bg-notice-300 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400',
59
59
  neutral:
60
- 'bg-neutral-200 text-neutral-800 data-[hovered]:bg-neutral-300 data-[pressed]:bg-neutral-400 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400'
60
+ 'bg-neutral-200 text-neutral-800 data-[hovered]:bg-neutral-300 data-[pressed]:bg-neutral-400 data-[disabled]:bg-neutral-50 data-[disabled]:text-neutral-400'
61
61
  },
62
62
  plain: {
63
63
  primary:
64
- 'bg-transparent text-primary-700 data-[hovered]:bg-primary-50 data-[pressed]:bg-primary-100 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400',
64
+ 'bg-transparent text-primary-700 data-[hovered]:bg-primary-50 data-[pressed]:bg-primary-100 data-[disabled]:bg-transparent data-[disabled]:text-neutral-400',
65
65
  negative:
66
- 'bg-transparent text-negative-700 data-[hovered]:bg-negative-50 data-[pressed]:bg-negative-100 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400',
66
+ 'bg-transparent text-negative-700 data-[hovered]:bg-negative-50 data-[pressed]:bg-negative-100 data-[disabled]:bg-transparent data-[disabled]:text-neutral-400',
67
67
  positive:
68
- 'bg-transparent text-positive-700 data-[hovered]:bg-positive-50 data-[pressed]:bg-positive-100 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400',
68
+ 'bg-transparent text-positive-700 data-[hovered]:bg-positive-50 data-[pressed]:bg-positive-100 data-[disabled]:bg-transparent data-[disabled]:text-neutral-400',
69
69
  notice:
70
- 'bg-transparent text-notice-700 data-[hovered]:bg-notice-50 data-[pressed]:bg-notice-100 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400',
70
+ 'bg-transparent text-notice-700 data-[hovered]:bg-notice-50 data-[pressed]:bg-notice-100 data-[disabled]:bg-transparent data-[disabled]:text-neutral-400',
71
71
  neutral:
72
- 'bg-transparent text-neutral-700 data-[hovered]:bg-neutral-50 data-[pressed]:bg-neutral-100 data-[disabled]:bg-neutral-200 data-[disabled]:text-neutral-400'
72
+ 'bg-transparent text-neutral-700 data-[hovered]:bg-neutral-50 data-[pressed]:bg-neutral-100 data-[disabled]:bg-transparent data-[disabled]:text-neutral-400'
73
73
  }
74
74
  }
75
75
 
@@ -105,7 +105,7 @@ const toggledVariantColorClasses: Record<ButtonVariant, Record<ButtonColor, stri
105
105
  }
106
106
 
107
107
  const focusClasses =
108
- 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 data-[focus-visible]:outline-2 data-[focus-visible]:outline-offset-2 data-[focus-visible]:outline-primary-600'
108
+ 'data-[focus-visible]:outline-2 data-[focus-visible]:outline-offset-2 data-[focus-visible]:outline-primary-600'
109
109
 
110
110
  export function getButtonClasses({
111
111
  variant = 'contained',
@@ -27,7 +27,7 @@ export const HeadlessButton = React.forwardRef<
27
27
  {...props}
28
28
  {...(props.disabled || props['data-disabled'] ? { ['data-disabled']: true } : {})}
29
29
  {...((isPressed && !props.disabled) || props['data-pressed'] ? { ['data-pressed']: true } : {})}
30
- aria-pressed={props['aria-pressed'] ?? (isPressed && !props.disabled)}
30
+ aria-pressed={isPressed && !props.disabled}
31
31
  ref={ref}
32
32
  />
33
33
  )
@@ -13,11 +13,11 @@ export type InlineAlertProps = {
13
13
  }
14
14
 
15
15
  const variantClasses = {
16
- info: 'bg-info-50 text-info-800 border-info-200',
16
+ info: 'bg-info-500/10 text-info-500 border-info-500/20',
17
17
  positive: 'bg-positive-50 text-positive-800 border-positive-200',
18
18
  notice: 'bg-notice-50 text-notice-800 border-notice-200',
19
19
  negative: 'bg-negative-50 text-negative-800 border-negative-200',
20
- neutral: 'bg-neutral-50 text-neutral-700 border-neutral-200'
20
+ neutral: 'bg-neutral-100 text-neutral-700 border-neutral-200'
21
21
  }
22
22
 
23
23
  const defaultIconNames = {
@@ -34,7 +34,7 @@ export function InlineAlert({ children, actions, variant, icon, className, title
34
34
  return (
35
35
  <div
36
36
  className={tw(
37
- 'rounded-lg border px-4 py-3 flex gap-3 items-start justify-between text-left',
37
+ 'rounded-md border px-4 py-3 flex gap-3 items-start justify-between text-left',
38
38
  variantClasses[variant],
39
39
  className
40
40
  )}
@@ -158,7 +158,7 @@ export const LabeledValue = React.forwardRef<HTMLDivElement, LabeledValueProps>(
158
158
  <LabeledValueLabel
159
159
  id={actualLabelId}
160
160
  className={tw(
161
- orientation === 'horizontal' ? 'shrink-0' : 'mb-1',
161
+ orientation === 'horizontal' ? 'flex-shrink-0' : 'mb-1',
162
162
  labelAlign === 'end' && 'text-right',
163
163
  labelClassName
164
164
  )}
@@ -474,7 +474,7 @@ export function LabeledValueLabel(props: React.ComponentProps<'label'>) {
474
474
  return (
475
475
  <label
476
476
  {...props}
477
- className={tw('block uppercase text-xs tracking-wider font-medium text-neutral-500', props.className)}
477
+ className={tw('block uppercase text-xs tracking-widest font-normal text-neutral-400', props.className)}
478
478
  />
479
479
  )
480
480
  }