@phsa.tec/design-system-react 0.1.5 → 0.1.8

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 (212) hide show
  1. package/README.md +390 -209
  2. package/dist/index.d.mts +485 -0
  3. package/dist/index.d.ts +485 -0
  4. package/dist/index.js +4259 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +4186 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +46 -6
  9. package/.eslintrc.json +0 -7
  10. package/.github/workflows/deploy-storybook.yml +0 -75
  11. package/.storybook/main.ts +0 -16
  12. package/.storybook/preview.ts +0 -15
  13. package/components.json +0 -21
  14. package/jest.config.ts +0 -25
  15. package/next.config.ts +0 -7
  16. package/postcss.config.mjs +0 -8
  17. package/public/file.svg +0 -1
  18. package/public/globe.svg +0 -1
  19. package/public/next.svg +0 -1
  20. package/public/vercel.svg +0 -1
  21. package/public/window.svg +0 -1
  22. package/src/app/columns.tsx +0 -178
  23. package/src/app/favicon.ico +0 -0
  24. package/src/app/fonts/GeistMonoVF.woff +0 -0
  25. package/src/app/fonts/GeistVF.woff +0 -0
  26. package/src/app/globals.css +0 -94
  27. package/src/app/layout.tsx +0 -35
  28. package/src/app/page.tsx +0 -7
  29. package/src/components/actions/AlertDialog/AlertDialog.tsx +0 -45
  30. package/src/components/actions/AlertDialog/alert-dialog.stories.tsx +0 -21
  31. package/src/components/actions/AlertDialog/index.ts +0 -1
  32. package/src/components/actions/Button/Button.stories.ts +0 -38
  33. package/src/components/actions/Button/Button.tsx +0 -23
  34. package/src/components/actions/Button/index.ts +0 -1
  35. package/src/components/actions/Collapsible/index.ts +0 -1
  36. package/src/components/actions/Dialog/Dialog.stories.tsx +0 -70
  37. package/src/components/actions/Dialog/Dialog.tsx +0 -87
  38. package/src/components/actions/Dialog/components/DialogWithActions/index.tsx +0 -40
  39. package/src/components/actions/Dialog/index.ts +0 -1
  40. package/src/components/actions/Steps/Steps.stories.tsx +0 -25
  41. package/src/components/actions/Steps/Steps.tsx +0 -51
  42. package/src/components/actions/Steps/index.ts +0 -1
  43. package/src/components/actions/index.ts +0 -5
  44. package/src/components/dataDisplay/Avatar/Avatar.stories.tsx +0 -22
  45. package/src/components/dataDisplay/Avatar/Avatar.tsx +0 -21
  46. package/src/components/dataDisplay/Avatar/index.ts +0 -2
  47. package/src/components/dataDisplay/Badge/Badge.stories.tsx +0 -36
  48. package/src/components/dataDisplay/Badge/index.ts +0 -1
  49. package/src/components/dataDisplay/Card/Card.stories.tsx +0 -24
  50. package/src/components/dataDisplay/Card/Card.tsx +0 -34
  51. package/src/components/dataDisplay/Card/index.ts +0 -1
  52. package/src/components/dataDisplay/DataPairList/DataPairList.tsx +0 -56
  53. package/src/components/dataDisplay/DataPairList/data-pair-list.stories.tsx +0 -87
  54. package/src/components/dataDisplay/DataPairList/index.ts +0 -2
  55. package/src/components/dataDisplay/DataPairList/types.ts +0 -10
  56. package/src/components/dataDisplay/DropDownMenu/index.ts +0 -1
  57. package/src/components/dataDisplay/ErrorMessage/ErrorMessage.tsx +0 -6
  58. package/src/components/dataDisplay/ErrorMessage/index.ts +0 -1
  59. package/src/components/dataDisplay/Icon/Icon.stories.tsx +0 -21
  60. package/src/components/dataDisplay/Icon/Icon.tsx +0 -47
  61. package/src/components/dataDisplay/Icon/index.ts +0 -1
  62. package/src/components/dataDisplay/Icon/types.ts +0 -6
  63. package/src/components/dataDisplay/Label/Label.stories.tsx +0 -21
  64. package/src/components/dataDisplay/Label/Label.tsx +0 -10
  65. package/src/components/dataDisplay/Label/index.ts +0 -1
  66. package/src/components/dataDisplay/Table/Table.tsx +0 -173
  67. package/src/components/dataDisplay/Table/columns.tsx +0 -223
  68. package/src/components/dataDisplay/Table/components/DynamicTable/data-table-column-header.tsx +0 -72
  69. package/src/components/dataDisplay/Table/components/DynamicTable/data-table-pagination.tsx +0 -91
  70. package/src/components/dataDisplay/Table/components/DynamicTable/data-table-toolbar.tsx +0 -17
  71. package/src/components/dataDisplay/Table/components/DynamicTable/data-table-view-options.tsx +0 -58
  72. package/src/components/dataDisplay/Table/components/DynamicTable/data-table.stories.tsx +0 -118
  73. package/src/components/dataDisplay/Table/components/DynamicTable/index.tsx +0 -136
  74. package/src/components/dataDisplay/Table/components/DynamicTable/types.ts +0 -43
  75. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-column-header.tsx +0 -71
  76. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-faceted-filter.tsx +0 -147
  77. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-pagination.tsx +0 -97
  78. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-row-actions.tsx +0 -78
  79. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-toolbar.tsx +0 -60
  80. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-view-options.tsx +0 -59
  81. package/src/components/dataDisplay/Table/custom/CustomTable/data-table.tsx +0 -145
  82. package/src/components/dataDisplay/Table/custom/CustomTable/data.ts +0 -71
  83. package/src/components/dataDisplay/Table/custom/CustomTable/index.tsx +0 -34
  84. package/src/components/dataDisplay/Table/custom/CustomTable/schema.ts +0 -11
  85. package/src/components/dataDisplay/Table/index.ts +0 -2
  86. package/src/components/dataDisplay/Table/table.stories.tsx +0 -147
  87. package/src/components/dataDisplay/Table/types.ts +0 -15
  88. package/src/components/dataDisplay/Tabs/Tabs.stories.tsx +0 -34
  89. package/src/components/dataDisplay/Tabs/Tabs.tsx +0 -53
  90. package/src/components/dataDisplay/Tabs/index.ts +0 -1
  91. package/src/components/dataDisplay/Text/Text.stories.tsx +0 -66
  92. package/src/components/dataDisplay/Text/Text.tsx +0 -56
  93. package/src/components/dataDisplay/Text/index.ts +0 -1
  94. package/src/components/dataDisplay/index.ts +0 -8
  95. package/src/components/dataInput/Input/components/Input/Input.stories.tsx +0 -99
  96. package/src/components/dataInput/Input/components/Input/InputBase.tsx +0 -50
  97. package/src/components/dataInput/Input/components/Input/__tests__/input.test.tsx +0 -38
  98. package/src/components/dataInput/Input/components/Input/index.tsx +0 -48
  99. package/src/components/dataInput/Input/components/Input/types.ts +0 -15
  100. package/src/components/dataInput/Input/components/InputBase/index.tsx +0 -31
  101. package/src/components/dataInput/Input/components/MaskInput/__tests__/mask-input.test.tsx +0 -77
  102. package/src/components/dataInput/Input/components/MaskInput/index.ts +0 -1
  103. package/src/components/dataInput/Input/components/MaskInput/mask-input.stories.tsx +0 -85
  104. package/src/components/dataInput/Input/components/MaskInput/mask-input.tsx +0 -73
  105. package/src/components/dataInput/Input/components/MultipleInput/MultipleInput.tsx +0 -105
  106. package/src/components/dataInput/Input/components/MultipleInput/MultipleInputBase.tsx +0 -100
  107. package/src/components/dataInput/Input/components/MultipleInput/MultipleMaskInput.tsx +0 -35
  108. package/src/components/dataInput/Input/components/MultipleInput/MultipleNumberInput.tsx +0 -35
  109. package/src/components/dataInput/Input/components/MultipleInput/__tests__/multiple-input.test.tsx +0 -152
  110. package/src/components/dataInput/Input/components/MultipleInput/index.ts +0 -2
  111. package/src/components/dataInput/Input/components/MultipleInput/multiple-input.stories.tsx +0 -96
  112. package/src/components/dataInput/Input/components/NumberInput/index.ts +0 -1
  113. package/src/components/dataInput/Input/components/NumberInput/number-input.stories.tsx +0 -76
  114. package/src/components/dataInput/Input/components/NumberInput/number-input.tsx +0 -68
  115. package/src/components/dataInput/Input/index.ts +0 -4
  116. package/src/components/dataInput/Select/MultiSelect/MultiSelect.stories.tsx +0 -119
  117. package/src/components/dataInput/Select/MultiSelect/MultiSelectBase.tsx +0 -135
  118. package/src/components/dataInput/Select/MultiSelect/index.tsx +0 -75
  119. package/src/components/dataInput/Select/Select.stories.tsx +0 -61
  120. package/src/components/dataInput/Select/Select.tsx +0 -73
  121. package/src/components/dataInput/Select/SelectBase.tsx +0 -58
  122. package/src/components/dataInput/Select/index.ts +0 -2
  123. package/src/components/dataInput/Switch/Switch.stories.tsx +0 -75
  124. package/src/components/dataInput/Switch/Switch.tsx +0 -52
  125. package/src/components/dataInput/Switch/index.ts +0 -1
  126. package/src/components/dataInput/checkbox/Checkbox.tsx +0 -57
  127. package/src/components/dataInput/checkbox/Checkbox_old.tsx +0 -58
  128. package/src/components/dataInput/checkbox/Checkout.stories.tsx +0 -62
  129. package/src/components/dataInput/checkbox/index.ts +0 -1
  130. package/src/components/dataInput/form/Form.tsx +0 -47
  131. package/src/components/dataInput/form/index.ts +0 -3
  132. package/src/components/dataInput/index.ts +0 -5
  133. package/src/components/feedback/ErrorLabel/index.tsx +0 -24
  134. package/src/components/feedback/Spinner/index.ts +0 -1
  135. package/src/components/feedback/Toast/Toast.stories.tsx +0 -45
  136. package/src/components/feedback/Toast/index.ts +0 -2
  137. package/src/components/feedback/index.ts +0 -2
  138. package/src/components/index.ts +0 -6
  139. package/src/components/layout/Crud/components/Table/index.tsx +0 -183
  140. package/src/components/layout/Crud/components/Table/types.ts +0 -15
  141. package/src/components/layout/Crud/crud.stories.tsx +0 -317
  142. package/src/components/layout/Crud/hook/useCrudLayout/index.tsx +0 -94
  143. package/src/components/layout/Crud/hook/useRequest/index.tsx +0 -156
  144. package/src/components/layout/Crud/index.tsx +0 -295
  145. package/src/components/layout/Crud/store/CrudLayoutStore.ts +0 -75
  146. package/src/components/layout/Crud/types.ts +0 -14
  147. package/src/components/layout/Drawer/CustomDrawer/index.tsx +0 -33
  148. package/src/components/layout/Drawer/Drawer.stories.tsx +0 -80
  149. package/src/components/layout/Drawer/index.ts +0 -2
  150. package/src/components/layout/PageLayout/PageLayout.stories.tsx +0 -42
  151. package/src/components/layout/PageLayout/index.tsx +0 -28
  152. package/src/components/layout/Separator/index.ts +0 -1
  153. package/src/components/layout/Sheet/Sheet.stories.tsx +0 -28
  154. package/src/components/layout/Sheet/Sheet.tsx +0 -22
  155. package/src/components/layout/Sheet/index.ts +0 -1
  156. package/src/components/layout/Sidebar/Sidebar.stories.tsx +0 -116
  157. package/src/components/layout/Sidebar/Sidebar.tsx +0 -50
  158. package/src/components/layout/Sidebar/components/app-sidebar.tsx +0 -203
  159. package/src/components/layout/Sidebar/components/footer-sidebar.tsx +0 -17
  160. package/src/components/layout/Sidebar/components/header-sidebar.tsx +0 -90
  161. package/src/components/layout/Sidebar/components/menus.tsx +0 -55
  162. package/src/components/layout/Sidebar/components/nav-projects.tsx +0 -88
  163. package/src/components/layout/Sidebar/components/nav-user.tsx +0 -114
  164. package/src/components/layout/Sidebar/components/team-switcher.tsx +0 -85
  165. package/src/components/layout/Sidebar/index.ts +0 -2
  166. package/src/components/layout/Sidebar/provider/index.tsx +0 -51
  167. package/src/components/layout/Tabs/Tabs.tsx +0 -51
  168. package/src/components/layout/Tabs/index.ts +0 -1
  169. package/src/components/layout/Tabs/tabs.stories.tsx +0 -57
  170. package/src/components/layout/index.ts +0 -6
  171. package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +0 -66
  172. package/src/components/navigation/Breadcrumbs/index.ts +0 -2
  173. package/src/components/navigation/index.ts +0 -1
  174. package/src/components/ui/alert-dialog.tsx +0 -141
  175. package/src/components/ui/alert.tsx +0 -59
  176. package/src/components/ui/avatar.tsx +0 -50
  177. package/src/components/ui/badge.tsx +0 -40
  178. package/src/components/ui/breadcrumb.tsx +0 -115
  179. package/src/components/ui/button.tsx +0 -57
  180. package/src/components/ui/card.tsx +0 -83
  181. package/src/components/ui/checkbox.tsx +0 -34
  182. package/src/components/ui/collapsible.tsx +0 -11
  183. package/src/components/ui/command.tsx +0 -153
  184. package/src/components/ui/dialog.tsx +0 -124
  185. package/src/components/ui/drawer.tsx +0 -117
  186. package/src/components/ui/dropdown-menu.tsx +0 -201
  187. package/src/components/ui/form.tsx +0 -179
  188. package/src/components/ui/input.tsx +0 -24
  189. package/src/components/ui/label.tsx +0 -30
  190. package/src/components/ui/popover.tsx +0 -33
  191. package/src/components/ui/select.tsx +0 -161
  192. package/src/components/ui/separator.tsx +0 -31
  193. package/src/components/ui/sheet.tsx +0 -140
  194. package/src/components/ui/sidebar.tsx +0 -763
  195. package/src/components/ui/skeleton.tsx +0 -15
  196. package/src/components/ui/sonner.tsx +0 -31
  197. package/src/components/ui/spinner.tsx +0 -54
  198. package/src/components/ui/switch.tsx +0 -33
  199. package/src/components/ui/table.tsx +0 -120
  200. package/src/components/ui/tabs.tsx +0 -55
  201. package/src/components/ui/toast.tsx +0 -130
  202. package/src/components/ui/toaster.tsx +0 -35
  203. package/src/components/ui/tooltip.tsx +0 -32
  204. package/src/hooks/use-conditional-controller.tsx +0 -35
  205. package/src/hooks/use-mask.tsx +0 -116
  206. package/src/hooks/use-mobile.tsx +0 -19
  207. package/src/hooks/use-toast.ts +0 -191
  208. package/src/index.ts +0 -1
  209. package/src/introduction.mdx +0 -132
  210. package/src/lib/utils.ts +0 -6
  211. package/tailwind.config.ts +0 -83
  212. package/tsconfig.json +0 -27
@@ -1,96 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/nextjs";
2
- import { MultipleInput } from "./MultipleInput";
3
- import { Form } from "@/components/ui/form";
4
- import { useForm } from "react-hook-form";
5
-
6
- const meta: Meta<typeof MultipleInput> = {
7
- title: "Data Input/Input/Multiple Input",
8
- component: MultipleInput,
9
- tags: ["autodocs"],
10
- parameters: {
11
- layout: "centered",
12
- },
13
- };
14
-
15
- export default meta;
16
- type Story = StoryObj<typeof MultipleInput>;
17
-
18
- export const Default: Story = {
19
- args: {
20
- label: "Multiple Input",
21
- name: "multipleInput",
22
- data: ["Item 1", "Item 2", "Item 3"],
23
- placeholder: "Multiple Input",
24
- withoutForm: true,
25
- onAdd: (data: string) => {
26
- console.log("Added:", data);
27
- },
28
- onRemove: (index: number) => {
29
- console.log("Removed index:", index);
30
- },
31
- },
32
- };
33
-
34
- export const WithError: Story = {
35
- args: {
36
- label: "Multiple Input",
37
- name: "multipleInput",
38
- error: "This is an error",
39
- data: ["Item 1", "Item 2", "Item 3"],
40
- placeholder: "Multiple Input",
41
- withoutForm: true,
42
- onAdd: (data: string) => {
43
- console.log("Added:", data);
44
- },
45
- onRemove: (index: number) => {
46
- console.log("Removed index:", index);
47
- },
48
- },
49
- };
50
-
51
- export const WithForm: Story = {
52
- args: {
53
- label: "Multiple Input",
54
- name: "client",
55
- placeholder: "Multiple Input",
56
- },
57
- decorators: [
58
- (Story: React.ComponentType) => {
59
- const form = useForm({
60
- defaultValues: {
61
- client: ["Item 1", "Item 2", "Item 3"],
62
- },
63
- });
64
-
65
- return (
66
- <Form {...form}>
67
- <Story />
68
- </Form>
69
- );
70
- },
71
- ],
72
- };
73
-
74
- export const WithMaskAndForm: Story = {
75
- args: {
76
- label: "Phone Numbers",
77
- name: "phoneNumbers",
78
- placeholder: "Enter phone number",
79
- mask: "(99) 99999-9999",
80
- },
81
- decorators: [
82
- (Story: React.ComponentType) => {
83
- const form = useForm({
84
- defaultValues: {
85
- phoneNumbers: ["(11) 99999-9999", "(21) 88888-8888"],
86
- },
87
- });
88
-
89
- return (
90
- <Form {...form}>
91
- <Story />
92
- </Form>
93
- );
94
- },
95
- ],
96
- };
@@ -1 +0,0 @@
1
- export * from "./number-input";
@@ -1,76 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/nextjs";
2
- import { NumberInput } from "./number-input";
3
- import { useForm } from "react-hook-form";
4
- import { Form } from "../../../../../components/ui/form";
5
-
6
- const meta = {
7
- title: "Data Input/Input/NumberInput",
8
- component: NumberInput,
9
- parameters: {
10
- layout: "centered",
11
- },
12
- tags: ["autodocs"],
13
- } satisfies Meta<typeof NumberInput>;
14
-
15
- export default meta;
16
- type Story = StoryObj<typeof meta>;
17
-
18
- export const Default: Story = {
19
- args: {
20
- placeholder: "0,00",
21
- label: "Valor",
22
- },
23
- };
24
-
25
- export const Currency: Story = {
26
- args: {
27
- placeholder: "0,00",
28
- label: "Preço",
29
- prefix: "R$ ",
30
- decimalScale: 2,
31
- fixedDecimalScale: false,
32
- value: 1234.56,
33
- },
34
- };
35
-
36
- export const Percentage: Story = {
37
- args: {
38
- placeholder: "0,00",
39
- label: "Porcentagem",
40
- suffix: "%",
41
- decimalScale: 1,
42
- fixedDecimalScale: true,
43
- value: 85.5,
44
- },
45
- };
46
-
47
- export const CustomFormat: Story = {
48
- args: {
49
- placeholder: "0",
50
- label: "Número",
51
- thousandSeparator: ",",
52
- decimalSeparator: ".",
53
- value: 1234567.89,
54
- },
55
- };
56
-
57
- export const WithForm = () => {
58
- const form = useForm({
59
- defaultValues: {
60
- price: 99.99,
61
- },
62
- });
63
-
64
- return (
65
- <Form {...form}>
66
- <form className="space-y-6">
67
- <NumberInput
68
- name="price"
69
- label="Preço"
70
- prefix="R$ "
71
- description="Digite o preço do produto"
72
- />
73
- </form>
74
- </Form>
75
- );
76
- };
@@ -1,68 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { NumericFormat, NumericFormatProps } from "react-number-format";
5
- import { InputBase, InputBaseProps } from "../InputBase";
6
- import { Input } from "../../../../ui/input";
7
-
8
- export type NumberInputProps = Omit<
9
- NumericFormatProps,
10
- "onChange" | "onValueChange"
11
- > &
12
- Omit<InputBaseProps, "children"> & {
13
- onChange?: (value: number) => void;
14
- "data-testid"?: string;
15
- component?: React.ReactNode;
16
- };
17
-
18
- export const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
19
- (props, ref) => {
20
- const {
21
- name,
22
- label,
23
- error,
24
- className,
25
- withoutForm,
26
- onChange,
27
- "data-testid": testId,
28
- component,
29
- ...inputProps
30
- } = props;
31
-
32
- const baseTestId = testId || name || "";
33
-
34
- return (
35
- <InputBase
36
- label={label}
37
- error={error}
38
- className={className}
39
- name={name}
40
- withoutForm={withoutForm}
41
- data-testid={baseTestId}
42
- >
43
- {({ onChange: onBaseChange, value }) => (
44
- <div
45
- className="flex w-full gap-3"
46
- data-testid={`number-input-wrapper-${baseTestId}`}
47
- >
48
- <NumericFormat
49
- value={value as number}
50
- customInput={Input}
51
- getInputRef={ref}
52
- onValueChange={({ floatValue }) => {
53
- const numberValue = floatValue;
54
- onBaseChange?.(numberValue);
55
- onChange?.(numberValue as number);
56
- }}
57
- data-testid={`${baseTestId}-number-input`}
58
- {...inputProps}
59
- />
60
- {component}
61
- </div>
62
- )}
63
- </InputBase>
64
- );
65
- }
66
- );
67
-
68
- NumberInput.displayName = "NumberInput";
@@ -1,4 +0,0 @@
1
- export * from "./components/Input";
2
- export * from "./components/NumberInput";
3
- export * from "./components/MaskInput";
4
- export * from "./components/MultipleInput";
@@ -1,119 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/nextjs";
2
- import { MultiSelect } from "./index";
3
- import { useState } from "react";
4
- import { Form } from "../../form";
5
- import { useForm } from "react-hook-form";
6
-
7
- const meta: Meta<typeof MultiSelect> = {
8
- title: "Data Input/MultiSelect",
9
- component: MultiSelect,
10
- tags: ["autodocs"],
11
- };
12
-
13
- export default meta;
14
- type Story = StoryObj<typeof MultiSelect>;
15
-
16
- const options = [
17
- { label: "Next.js", value: "nextjs" },
18
- { label: "SvelteKit", value: "sveltekit" },
19
- { label: "Nuxt.js", value: "nuxtjs" },
20
- { label: "Remix", value: "remix" },
21
- { label: "Astro", value: "astro" },
22
- ];
23
-
24
- const MultiSelectWithHooks = () => {
25
- const [selected, setSelected] = useState<string[]>([]);
26
-
27
- return (
28
- <MultiSelect
29
- label="MultiSelect"
30
- options={options}
31
- selected={selected}
32
- onChange={setSelected}
33
- placeholder="Select frameworks..."
34
- />
35
- );
36
- };
37
-
38
- export const Default: Story = {
39
- render: () => <MultiSelectWithHooks />,
40
- };
41
-
42
- export const Preselected: Story = {
43
- render: () => {
44
- const [selected, setSelected] = useState<string[]>(["nextjs", "nuxtjs"]);
45
-
46
- return (
47
- <MultiSelect
48
- options={options}
49
- selected={selected}
50
- onChange={setSelected}
51
- placeholder="Select frameworks..."
52
- />
53
- );
54
- },
55
- };
56
-
57
- export const CustomPlaceholder: Story = {
58
- render: () => {
59
- const [selected, setSelected] = useState<string[]>([]);
60
-
61
- return (
62
- <MultiSelect
63
- options={options}
64
- selected={selected}
65
- onChange={setSelected}
66
- placeholder="Choose your favorite frameworks..."
67
- />
68
- );
69
- },
70
- };
71
-
72
- export const ManyOptions: Story = {
73
- render: () => {
74
- const [selected, setSelected] = useState<string[]>([]);
75
- const manyOptions = [
76
- ...options,
77
- { label: "React", value: "react" },
78
- { label: "Vue", value: "vue" },
79
- { label: "Angular", value: "angular" },
80
- { label: "Solid", value: "solid" },
81
- { label: "Qwik", value: "qwik" },
82
- { label: "Ember", value: "ember" },
83
- { label: "Preact", value: "preact" },
84
- { label: "Alpine", value: "alpine" },
85
- ];
86
-
87
- return (
88
- <MultiSelect
89
- options={manyOptions}
90
- selected={selected}
91
- onChange={setSelected}
92
- placeholder="Select frameworks..."
93
- />
94
- );
95
- },
96
- };
97
-
98
- export const WithForm: Story = {
99
- args: {
100
- name: "multiSelect",
101
- options: [{ label: "Next.js", value: "nextjs" }],
102
- label: "MultiSelect",
103
- required: true,
104
- },
105
- decorators: [
106
- (Story: React.ComponentType) => {
107
- const form = useForm({
108
- defaultValues: {
109
- multiSelect: ["nextjs"],
110
- },
111
- });
112
- return (
113
- <Form {...form}>
114
- <Story />
115
- </Form>
116
- );
117
- },
118
- ],
119
- };
@@ -1,135 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { Search, X } from "lucide-react";
5
- import { cn } from "../../../../lib/utils";
6
- import * as Popover from "@radix-ui/react-popover";
7
-
8
- export type MultiSelectBaseProps = {
9
- options: { label: string; value: string }[];
10
- selected?: string[];
11
- onChange?: (values: string[]) => void;
12
- placeholder?: string;
13
- className?: string;
14
- };
15
-
16
- export function MultiSelectBase({
17
- options = [],
18
- selected = [],
19
- onChange = () => {},
20
- placeholder = "Select frameworks...",
21
- className,
22
- }: MultiSelectBaseProps) {
23
- const [open, setOpen] = React.useState(false);
24
- const [inputValue, setInputValue] = React.useState("");
25
- const inputRef = React.useRef<HTMLInputElement>(null);
26
-
27
- const handleRemoveItem = (valueToRemove: string) => {
28
- onChange(selected.filter((value) => value !== valueToRemove));
29
- };
30
-
31
- const handleSelectItem = (value: string) => {
32
- if (selected.includes(value)) {
33
- onChange(selected.filter((v) => v !== value));
34
- } else {
35
- onChange([...selected, value]);
36
- }
37
- };
38
-
39
- const filteredOptions = options.filter((option) =>
40
- option.label.toLowerCase().includes(inputValue.toLowerCase())
41
- );
42
-
43
- return (
44
- <Popover.Root open={open} onOpenChange={setOpen}>
45
- <Popover.Trigger asChild>
46
- <div
47
- className={cn(
48
- "flex min-h-[40px] w-full flex-wrap gap-1.5 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
49
- className
50
- )}
51
- >
52
- {selected.map((value) => {
53
- const option = options.find((o) => o.value === value);
54
- if (!option) return null;
55
-
56
- return (
57
- <div
58
- key={value}
59
- className="flex items-center gap-1 rounded-md bg-secondary px-2 py-1 text-sm text-secondary-foreground"
60
- >
61
- {option.label}
62
- <button
63
- type="button"
64
- onClick={(e) => {
65
- e.stopPropagation();
66
- handleRemoveItem(value);
67
- }}
68
- className="rounded-full hover:bg-secondary-foreground/20"
69
- >
70
- <X className="h-3 w-3" />
71
- <span className="sr-only">Remove {option.label}</span>
72
- </button>
73
- </div>
74
- );
75
- })}
76
- <div className="flex-1">
77
- {selected.length === 0 && (
78
- <span className="text-muted-foreground">{placeholder}</span>
79
- )}
80
- </div>
81
- </div>
82
- </Popover.Trigger>
83
-
84
- <Popover.Portal>
85
- <Popover.Content
86
- className="w-[--radix-popover-trigger-width] z-50 mt-1 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95"
87
- align="start"
88
- sideOffset={4}
89
- >
90
- <div className="flex items-center border-b border-border px-3 py-2">
91
- <Search className="h-4 w-4 text-muted-foreground" />
92
- <input
93
- ref={inputRef}
94
- value={inputValue}
95
- onChange={(e) => setInputValue(e.target.value)}
96
- placeholder="Search..."
97
- className="flex-1 bg-transparent px-2 outline-none placeholder:text-muted-foreground"
98
- />
99
- </div>
100
- <div className="max-h-[200px] overflow-auto">
101
- {filteredOptions.length === 0 ? (
102
- <div className="px-2 py-4 text-center text-sm text-muted-foreground">
103
- No results found.
104
- </div>
105
- ) : (
106
- filteredOptions.map((option) => (
107
- <div
108
- key={option.value}
109
- onClick={() => handleSelectItem(option.value)}
110
- className={cn(
111
- "flex cursor-pointer items-center gap-2 px-4 py-2 text-sm hover:bg-accent",
112
- selected.includes(option.value) && "bg-accent"
113
- )}
114
- >
115
- <div
116
- className={cn(
117
- "flex h-4 w-4 items-center justify-center rounded border border-primary",
118
- selected.includes(option.value) &&
119
- "bg-primary text-primary-foreground"
120
- )}
121
- >
122
- {selected.includes(option.value) && (
123
- <span className="text-[10px]">✓</span>
124
- )}
125
- </div>
126
- {option.label}
127
- </div>
128
- ))
129
- )}
130
- </div>
131
- </Popover.Content>
132
- </Popover.Portal>
133
- </Popover.Root>
134
- );
135
- }
@@ -1,75 +0,0 @@
1
- "use client";
2
- import React from "react";
3
- import { Label } from "../../../../components/dataDisplay/Label";
4
- import { MultiSelectBase, MultiSelectBaseProps } from "./MultiSelectBase";
5
- import {
6
- FormControl,
7
- FormField,
8
- FormItem,
9
- FormLabel,
10
- FormMessage,
11
- } from "../../../../components/ui/form";
12
- import { useFormContext } from "react-hook-form";
13
-
14
- type MultiSelectProps = MultiSelectBaseProps & {
15
- label?: string;
16
- name?: string;
17
- withoutForm?: boolean;
18
- className?: string;
19
- required?: boolean;
20
- "data-testid"?: string;
21
- };
22
-
23
- export function MultiSelect({
24
- label,
25
- name,
26
- withoutForm,
27
- className,
28
- required,
29
- "data-testid": testId,
30
- ...props
31
- }: MultiSelectProps) {
32
- const form = useFormContext();
33
- const hasForm = !withoutForm && !!form && !!name;
34
-
35
- if (!hasForm)
36
- return (
37
- <div className="grid w-full items-center gap-3">
38
- {label && <Label htmlFor={name}>{label}</Label>}
39
- <MultiSelectBase {...props} />
40
- </div>
41
- );
42
-
43
- return (
44
- <FormField
45
- control={form.control}
46
- name={name}
47
- render={({ field: { value, ...rest } }) => {
48
- return (
49
- <FormItem
50
- className={className}
51
- data-testid={testId ? `form-item-${testId}` : undefined}
52
- >
53
- {label && (
54
- <FormLabel
55
- htmlFor={name}
56
- data-testid={testId ? `form-label-${testId}` : undefined}
57
- >
58
- {`${label}${required ? " *" : ""}`}
59
- </FormLabel>
60
- )}
61
- <FormControl>
62
- <div className="flex w-full items-center space-x-2">
63
- <MultiSelectBase {...props} {...rest} selected={value} />
64
- </div>
65
- </FormControl>
66
- <FormMessage
67
- role="alert"
68
- data-testid={testId ? `form-message-${testId}` : undefined}
69
- />
70
- </FormItem>
71
- );
72
- }}
73
- />
74
- );
75
- }
@@ -1,61 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/nextjs";
2
- import { Select } from "./index";
3
- import { useForm } from "react-hook-form";
4
- import { Button } from "../../../components/actions";
5
- import { Form } from "../form";
6
-
7
- const meta: Meta<typeof Select> = {
8
- title: "Data Input/Select",
9
- component: Select,
10
- tags: ["autodocs"],
11
- parameters: {
12
- layout: "centered",
13
- },
14
- argTypes: {
15
- options: { control: "object" },
16
- placeholder: { control: "text" },
17
- label: { control: "text" },
18
- value: { control: "text" },
19
- onChange: { action: "changed" },
20
- },
21
- };
22
-
23
- export default meta;
24
- type Story = StoryObj<typeof Select>;
25
-
26
- export const Default: Story = {
27
- args: {
28
- options: [
29
- { value: "option1", label: "Option 1" },
30
- { value: "option2", label: "Option 2" },
31
- ],
32
- placeholder: "Select an option",
33
- label: "Select Label",
34
- },
35
- };
36
-
37
- export const WithForm: Story = {
38
- args: {
39
- options: [
40
- { value: "option1", label: "Option 1" },
41
- { value: "option2", label: "Option 2" },
42
- ],
43
- placeholder: "Select an option",
44
- label: "Select Label",
45
- name: "select",
46
- },
47
- render: (args) => {
48
- const form = useForm();
49
- const onSubmit = (data: unknown) => console.log(data);
50
- return (
51
- <Form {...form}>
52
- <form className="space-y-6" onSubmit={form.handleSubmit(onSubmit)}>
53
- <Select {...args} />
54
- <Button type="button" onClick={() => console.log(form.getValues())}>
55
- Submit
56
- </Button>
57
- </form>
58
- </Form>
59
- );
60
- },
61
- };