@fuf-stack/pixels 0.0.3 → 0.0.5

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 (48) hide show
  1. package/.eslintrc +16 -0
  2. package/.storybook/main.ts +8 -0
  3. package/.storybook/preview-head.html +3 -0
  4. package/.storybook/preview.tsx +7 -0
  5. package/CHANGELOG.md +36 -0
  6. package/Globals.d.ts +3 -0
  7. package/dist/Button/Button.cjs +3 -3
  8. package/dist/Button/Button.cjs.map +1 -1
  9. package/dist/Button/Button.js +2 -2
  10. package/dist/Button/index.cjs +5 -5
  11. package/dist/Button/index.cjs.map +1 -1
  12. package/dist/Button/index.js +3 -3
  13. package/dist/Button/index.js.map +1 -1
  14. package/dist/Button/subcomponents/LoadingSpinner.cjs +1 -1
  15. package/dist/Button/subcomponents/LoadingSpinner.cjs.map +1 -1
  16. package/dist/Button/subcomponents/LoadingSpinner.js +1 -1
  17. package/dist/Card/Card.cjs +1 -1
  18. package/dist/Card/Card.cjs.map +1 -1
  19. package/dist/Card/Card.js +1 -1
  20. package/dist/Card/index.cjs +3 -3
  21. package/dist/Card/index.cjs.map +1 -1
  22. package/dist/Card/index.js +2 -2
  23. package/dist/Card/index.js.map +1 -1
  24. package/dist/{chunk-ZXTDGCUF.js → chunk-WQN756O7.js} +3 -3
  25. package/dist/chunk-WQN756O7.js.map +1 -0
  26. package/dist/{chunk-XPTSDDXG.js → chunk-XWTXH2TR.js} +2 -2
  27. package/dist/chunk-XWTXH2TR.js.map +1 -0
  28. package/dist/{chunk-ZFEVTQWW.js → chunk-YOPQSZ46.js} +2 -2
  29. package/dist/chunk-YOPQSZ46.js.map +1 -0
  30. package/package.json +9 -8
  31. package/src/components/Button/Button.stories.tsx +75 -0
  32. package/src/components/Button/Button.test.tsx +9 -0
  33. package/src/components/Button/Button.tsx +74 -0
  34. package/src/components/Button/__snapshots__/Button.test.tsx.snap +235 -0
  35. package/src/components/Button/index.ts +7 -0
  36. package/src/components/Button/subcomponents/LoadingSpinner.tsx +26 -0
  37. package/src/components/Card/Card.stories.tsx +56 -0
  38. package/src/components/Card/Card.test.tsx +9 -0
  39. package/src/components/Card/Card.tsx +120 -0
  40. package/src/components/Card/__snapshots__/Card.test.tsx.snap +94 -0
  41. package/src/components/Card/index.ts +3 -0
  42. package/tailwind.config.js +7 -0
  43. package/tsconfig.json +7 -0
  44. package/tsup.config.ts +15 -0
  45. package/vitest.config.ts +9 -0
  46. package/dist/chunk-XPTSDDXG.js.map +0 -1
  47. package/dist/chunk-ZFEVTQWW.js.map +0 -1
  48. package/dist/chunk-ZXTDGCUF.js.map +0 -1
@@ -0,0 +1,74 @@
1
+ import type { ButtonProps as NextButtonProps } from '@nextui-org/button';
2
+ import type { ReactNode } from 'react';
3
+
4
+ import { Button as NextButton } from '@nextui-org/button';
5
+ import cn from 'classnames';
6
+
7
+ import LoadingSpinner from './subcomponents/LoadingSpinner';
8
+
9
+ export interface ButtonProps {
10
+ /** sets HTML aria-label attribute */
11
+ ariaLabel?: string;
12
+ /** child components */
13
+ children?: ReactNode;
14
+ /** CSS class name */
15
+ className?: string | string[];
16
+ /** next ui button color */
17
+ color?: NextButtonProps['color'];
18
+ /** disables function of the button. */
19
+ disabled?: boolean;
20
+ /** If set loading animation is shown */
21
+ loading?: boolean;
22
+ /** optional icon */
23
+ icon?: ReactNode;
24
+ /** on click event */
25
+ onClick?: NextButtonProps['onPress'];
26
+ /** 3 size options */
27
+ size?: NextButtonProps['size'];
28
+ /** HTML data-testid attribute used in e2e tests */
29
+ testId?: string;
30
+ /** sets the button type. */
31
+ type?: 'button' | 'submit' | 'reset' | undefined;
32
+ /** next ui button variants */
33
+ variant?: NextButtonProps['variant'];
34
+ }
35
+
36
+ /**
37
+ * Button component based on [NextUI Button](https://nextui.org/docs/components/button)
38
+ */
39
+ const Button = ({
40
+ ariaLabel = undefined,
41
+ children = undefined,
42
+ className = undefined,
43
+ color = 'default',
44
+ disabled = false,
45
+ icon = undefined,
46
+ loading = false,
47
+ onClick = undefined,
48
+ size = undefined,
49
+ testId = undefined,
50
+ type = undefined,
51
+ variant = 'solid',
52
+ }: ButtonProps) => {
53
+ return (
54
+ <NextButton
55
+ aria-label={ariaLabel}
56
+ className={cn(className)}
57
+ color={color}
58
+ data-testid={testId}
59
+ isDisabled={disabled}
60
+ isIconOnly={!!(icon && !children)}
61
+ isLoading={loading}
62
+ onPress={onClick}
63
+ size={size}
64
+ spinner={<LoadingSpinner />}
65
+ type={type}
66
+ variant={variant}
67
+ >
68
+ {icon}
69
+ {children}
70
+ </NextButton>
71
+ );
72
+ };
73
+
74
+ export default Button;
@@ -0,0 +1,235 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Story Snapshots > AllColors 1`] = `
4
+ <div>
5
+ <div
6
+ style="margin-top: 10px;"
7
+ >
8
+ <button
9
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default text-default-foreground data-[hover=true]:opacity-hover"
10
+ type="button"
11
+ >
12
+ default
13
+ </button>
14
+ </div>
15
+ <div
16
+ style="margin-top: 10px;"
17
+ >
18
+ <button
19
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-primary text-primary-foreground data-[hover=true]:opacity-hover"
20
+ type="button"
21
+ >
22
+ primary
23
+ </button>
24
+ </div>
25
+ <div
26
+ style="margin-top: 10px;"
27
+ >
28
+ <button
29
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-secondary text-secondary-foreground data-[hover=true]:opacity-hover"
30
+ type="button"
31
+ >
32
+ secondary
33
+ </button>
34
+ </div>
35
+ <div
36
+ style="margin-top: 10px;"
37
+ >
38
+ <button
39
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-success text-success-foreground data-[hover=true]:opacity-hover"
40
+ type="button"
41
+ >
42
+ success
43
+ </button>
44
+ </div>
45
+ <div
46
+ style="margin-top: 10px;"
47
+ >
48
+ <button
49
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-warning text-warning-foreground data-[hover=true]:opacity-hover"
50
+ type="button"
51
+ >
52
+ warning
53
+ </button>
54
+ </div>
55
+ <div
56
+ style="margin-top: 10px;"
57
+ >
58
+ <button
59
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-danger text-danger-foreground data-[hover=true]:opacity-hover"
60
+ type="button"
61
+ >
62
+ danger
63
+ </button>
64
+ </div>
65
+ </div>
66
+ `;
67
+
68
+ exports[`Story Snapshots > AllSizes 1`] = `
69
+ <div>
70
+ <div
71
+ style="margin-top: 10px;"
72
+ >
73
+ <button
74
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-3 min-w-unit-16 h-unit-8 text-tiny gap-unit-2 rounded-small [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default text-default-foreground data-[hover=true]:opacity-hover"
75
+ type="button"
76
+ >
77
+ sm
78
+ </button>
79
+ </div>
80
+ <div
81
+ style="margin-top: 10px;"
82
+ >
83
+ <button
84
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default text-default-foreground data-[hover=true]:opacity-hover"
85
+ type="button"
86
+ >
87
+ md
88
+ </button>
89
+ </div>
90
+ <div
91
+ style="margin-top: 10px;"
92
+ >
93
+ <button
94
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-6 min-w-unit-24 h-unit-12 text-medium gap-unit-3 rounded-large [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default text-default-foreground data-[hover=true]:opacity-hover"
95
+ type="button"
96
+ >
97
+ lg
98
+ </button>
99
+ </div>
100
+ </div>
101
+ `;
102
+
103
+ exports[`Story Snapshots > AllVariants 1`] = `
104
+ <div>
105
+ <div
106
+ style="margin-top: 10px;"
107
+ >
108
+ <button
109
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default text-default-foreground data-[hover=true]:opacity-hover"
110
+ type="button"
111
+ >
112
+ solid
113
+ </button>
114
+ </div>
115
+ <div
116
+ style="margin-top: 10px;"
117
+ >
118
+ <button
119
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 border-medium px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-transparent border-default text-foreground data-[hover=true]:opacity-hover"
120
+ type="button"
121
+ >
122
+ bordered
123
+ </button>
124
+ </div>
125
+ <div
126
+ style="margin-top: 10px;"
127
+ >
128
+ <button
129
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-transparent text-default-foreground data-[hover=true]:bg-default/40"
130
+ type="button"
131
+ >
132
+ light
133
+ </button>
134
+ </div>
135
+ <div
136
+ style="margin-top: 10px;"
137
+ >
138
+ <button
139
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default/40 text-default-foreground data-[hover=true]:opacity-hover"
140
+ type="button"
141
+ >
142
+ flat
143
+ </button>
144
+ </div>
145
+ <div
146
+ style="margin-top: 10px;"
147
+ >
148
+ <button
149
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 border-medium px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none border-default bg-default-100 text-default-foreground data-[hover=true]:opacity-hover"
150
+ type="button"
151
+ >
152
+ faded
153
+ </button>
154
+ </div>
155
+ <div
156
+ style="margin-top: 10px;"
157
+ >
158
+ <button
159
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none shadow-lg shadow-default/50 bg-default text-default-foreground data-[hover=true]:opacity-hover"
160
+ type="button"
161
+ >
162
+ shadow
163
+ </button>
164
+ </div>
165
+ <div
166
+ style="margin-top: 10px;"
167
+ >
168
+ <button
169
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 border-medium bg-transparent px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none border-default text-default-foreground hover:!bg-default"
170
+ type="button"
171
+ >
172
+ ghost
173
+ </button>
174
+ </div>
175
+ </div>
176
+ `;
177
+
178
+ exports[`Story Snapshots > Default 1`] = `
179
+ <div>
180
+ <button
181
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default text-default-foreground data-[hover=true]:opacity-hover"
182
+ data-testid="some-test-id"
183
+ type="button"
184
+ >
185
+ Button
186
+ </button>
187
+ </div>
188
+ `;
189
+
190
+ exports[`Story Snapshots > Disabled 1`] = `
191
+ <div>
192
+ <button
193
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium opacity-disabled pointer-events-none [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default text-default-foreground data-[hover=true]:opacity-hover"
194
+ data-disabled="true"
195
+ disabled=""
196
+ type="button"
197
+ >
198
+ Button
199
+ </button>
200
+ </div>
201
+ `;
202
+
203
+ exports[`Story Snapshots > Loading 1`] = `
204
+ <div>
205
+ <button
206
+ class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-medium opacity-disabled pointer-events-none [&>svg]:max-w-[theme(spacing.unit-8)] data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none bg-default text-default-foreground data-[hover=true]:opacity-hover"
207
+ data-disabled="true"
208
+ data-loading="true"
209
+ disabled=""
210
+ type="button"
211
+ >
212
+ <svg
213
+ class="animate-spin h-5 w-5 text-current"
214
+ fill="none"
215
+ viewBox="0 0 24 24"
216
+ xmlns="http://www.w3.org/2000/svg"
217
+ >
218
+ <circle
219
+ class="opacity-25"
220
+ cx="12"
221
+ cy="12"
222
+ r="10"
223
+ stroke="currentColor"
224
+ stroke-width="4"
225
+ />
226
+ <path
227
+ class="opacity-75"
228
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
229
+ fill="currentColor"
230
+ />
231
+ </svg>
232
+ Button
233
+ </button>
234
+ </div>
235
+ `;
@@ -0,0 +1,7 @@
1
+ import type { ButtonProps } from './Button';
2
+
3
+ import Button from './Button';
4
+
5
+ export default Button;
6
+
7
+ export type { ButtonProps };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * svg loading spinner for button
3
+ * @see https://nextui.org/docs/components/button#loading
4
+ * */
5
+ export default () => (
6
+ <svg
7
+ className="animate-spin h-5 w-5 text-current"
8
+ fill="none"
9
+ viewBox="0 0 24 24"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ >
12
+ <circle
13
+ className="opacity-25"
14
+ cx="12"
15
+ cy="12"
16
+ r="10"
17
+ stroke="currentColor"
18
+ strokeWidth="4"
19
+ />
20
+ <path
21
+ className="opacity-75"
22
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
23
+ fill="currentColor"
24
+ />
25
+ </svg>
26
+ );
@@ -0,0 +1,56 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import Card from './Card';
4
+
5
+ const meta: Meta<typeof Card> = {
6
+ title: 'pixels/Card',
7
+ component: Card,
8
+ };
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof Card>;
12
+
13
+ export const Default: Story = {
14
+ args: {
15
+ children: (
16
+ <p className="w-96">
17
+ Lorem commodo nulla adipisicing cillum Lorem sunt laboris exercitation
18
+ esse. Exercitation ut cillum cupidatat deserunt occaecat pariatur
19
+ laborum ut reprehenderit veniam. Culpa fugiat aliqua consectetur sit
20
+ esse.
21
+ </p>
22
+ ),
23
+ },
24
+ };
25
+
26
+ export const FooterAndHeader: Story = {
27
+ args: {
28
+ footer: 'Footer',
29
+ header: 'Header',
30
+ children: (
31
+ <p className="w-96">
32
+ Lorem commodo nulla adipisicing cillum Lorem sunt laboris exercitation
33
+ esse. Exercitation ut cillum cupidatat deserunt occaecat pariatur
34
+ laborum ut reprehenderit veniam. Culpa fugiat aliqua consectetur sit
35
+ esse.
36
+ </p>
37
+ ),
38
+ },
39
+ };
40
+
41
+ export const CustomSlotStyles: Story = {
42
+ args: {
43
+ footer: 'Footer',
44
+ header: 'Header',
45
+ children: (
46
+ <p className="w-96">
47
+ Lorem commodo nulla adipisicing cillum Lorem sunt laboris exercitatio
48
+ </p>
49
+ ),
50
+ className: {
51
+ base: 'text-blue-400',
52
+ body: 'text-green-400',
53
+ divider: 'bg-yellow-400',
54
+ },
55
+ },
56
+ };
@@ -0,0 +1,9 @@
1
+ import { describe } from 'vitest';
2
+
3
+ import storySnapshots from 'storybook-config/story-snapshots';
4
+
5
+ import * as stories from './Card.stories';
6
+
7
+ describe('Story Snapshots', () => {
8
+ storySnapshots(stories);
9
+ });
@@ -0,0 +1,120 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { VariantProps } from 'tailwind-variants';
3
+
4
+ import {
5
+ Card as NextCard,
6
+ CardBody as NextCardBody,
7
+ CardFooter as NextCardFooter,
8
+ CardHeader as NextCardHeader,
9
+ } from '@nextui-org/card';
10
+ import { Divider as NextDivider } from '@nextui-org/divider';
11
+ import createDebug from 'debug';
12
+ import { tv } from 'tailwind-variants';
13
+
14
+ const debug = createDebug('component:Card');
15
+
16
+ // card styling variants
17
+ export const cardVariants = tv({
18
+ slots: {
19
+ base: 'border border-slate-300',
20
+ body: '',
21
+ divider: 'my-0 bg-slate-300',
22
+ footer: '',
23
+ header: 'text-base font-semibold',
24
+ },
25
+ });
26
+
27
+ type CardVariantProps = VariantProps<typeof cardVariants>;
28
+ type CardVariantSlots = Partial<
29
+ Record<keyof ReturnType<typeof cardVariants>, string>
30
+ >;
31
+
32
+ export interface CardProps extends CardVariantProps {
33
+ /** child components */
34
+ children?: ReactNode;
35
+ /** CSS class name */
36
+ className?: string | CardVariantSlots;
37
+ /** footer content */
38
+ footer?: ReactNode;
39
+ /** header content */
40
+ header?: ReactNode;
41
+ /** HTML data-testid attribute used in e2e tests */
42
+ testId?: string;
43
+ }
44
+
45
+ /**
46
+ * Card component based on [NextUI Card](https://nextui.org/docs/components/card)
47
+ */
48
+ const Card = ({
49
+ children = null,
50
+ className = undefined,
51
+ testId = undefined,
52
+ header = undefined,
53
+ footer = undefined,
54
+ }: CardProps) => {
55
+ debug('Card', { className, testId });
56
+ const {
57
+ base: baseSlot,
58
+ body: bodySlot,
59
+ divider: dividerSlot,
60
+ footer: footerSlot,
61
+ header: headerSlot,
62
+ } = cardVariants();
63
+
64
+ return (
65
+ <NextCard
66
+ data-testid={testId && `card_${testId}`}
67
+ className={baseSlot({
68
+ className: typeof className === 'object' ? className.base : className,
69
+ })}
70
+ fullWidth
71
+ radius="sm"
72
+ shadow="none"
73
+ >
74
+ {header && (
75
+ <>
76
+ <NextCardHeader
77
+ data-testid={testId && `card_header_${testId}`}
78
+ className={headerSlot({
79
+ className: typeof className === 'object' && className.header,
80
+ })}
81
+ >
82
+ {header}
83
+ </NextCardHeader>
84
+ <NextDivider
85
+ className={dividerSlot({
86
+ className: typeof className === 'object' && className.divider,
87
+ })}
88
+ />
89
+ </>
90
+ )}
91
+ <NextCardBody
92
+ data-testid={testId && `card_body_${testId}`}
93
+ className={bodySlot({
94
+ className: typeof className === 'object' && className.body,
95
+ })}
96
+ >
97
+ {children}
98
+ </NextCardBody>
99
+ {footer && (
100
+ <>
101
+ <NextDivider
102
+ className={dividerSlot({
103
+ className: typeof className === 'object' && className.divider,
104
+ })}
105
+ />
106
+ <NextCardFooter
107
+ data-testid={testId && `card_footer_${testId}`}
108
+ className={footerSlot({
109
+ className: typeof className === 'object' && className.footer,
110
+ })}
111
+ >
112
+ {footer}
113
+ </NextCardFooter>
114
+ </>
115
+ )}
116
+ </NextCard>
117
+ );
118
+ };
119
+
120
+ export default Card;
@@ -0,0 +1,94 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Story Snapshots > CustomSlotStyles 1`] = `
4
+ <div>
5
+ <div
6
+ class="flex flex-col relative overflow-hidden height-auto box-border bg-content1 outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 shadow-none rounded-small w-full transition-transform-background motion-reduce:transition-none border border-slate-300 text-blue-400"
7
+ tabindex="-1"
8
+ >
9
+ <div
10
+ class="flex p-3 z-10 w-full justify-start items-center shrink-0 overflow-inherit color-inherit subpixel-antialiased rounded-t-small text-base font-semibold"
11
+ >
12
+ Header
13
+ </div>
14
+ <hr
15
+ class="shrink-0 border-none w-full h-divider my-0 bg-yellow-400"
16
+ role="separator"
17
+ />
18
+ <div
19
+ class="relative flex w-full p-3 flex-auto flex-col place-content-inherit align-items-inherit h-auto break-words text-left overflow-y-auto subpixel-antialiased text-green-400"
20
+ >
21
+ <p
22
+ class="w-96"
23
+ >
24
+ Lorem commodo nulla adipisicing cillum Lorem sunt laboris exercitatio
25
+ </p>
26
+ </div>
27
+ <hr
28
+ class="shrink-0 border-none w-full h-divider my-0 bg-yellow-400"
29
+ role="separator"
30
+ />
31
+ <div
32
+ class="p-3 h-auto flex w-full items-center overflow-hidden color-inherit subpixel-antialiased rounded-b-small"
33
+ >
34
+ Footer
35
+ </div>
36
+ </div>
37
+ </div>
38
+ `;
39
+
40
+ exports[`Story Snapshots > Default 1`] = `
41
+ <div>
42
+ <div
43
+ class="flex flex-col relative overflow-hidden height-auto text-foreground box-border bg-content1 outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 shadow-none rounded-small w-full transition-transform-background motion-reduce:transition-none border border-slate-300"
44
+ tabindex="-1"
45
+ >
46
+ <div
47
+ class="relative flex w-full p-3 flex-auto flex-col place-content-inherit align-items-inherit h-auto break-words text-left overflow-y-auto subpixel-antialiased"
48
+ >
49
+ <p
50
+ class="w-96"
51
+ >
52
+ Lorem commodo nulla adipisicing cillum Lorem sunt laboris exercitation esse. Exercitation ut cillum cupidatat deserunt occaecat pariatur laborum ut reprehenderit veniam. Culpa fugiat aliqua consectetur sit esse.
53
+ </p>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ `;
58
+
59
+ exports[`Story Snapshots > FooterAndHeader 1`] = `
60
+ <div>
61
+ <div
62
+ class="flex flex-col relative overflow-hidden height-auto text-foreground box-border bg-content1 outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 shadow-none rounded-small w-full transition-transform-background motion-reduce:transition-none border border-slate-300"
63
+ tabindex="-1"
64
+ >
65
+ <div
66
+ class="flex p-3 z-10 w-full justify-start items-center shrink-0 overflow-inherit color-inherit subpixel-antialiased rounded-t-small text-base font-semibold"
67
+ >
68
+ Header
69
+ </div>
70
+ <hr
71
+ class="shrink-0 border-none w-full h-divider my-0 bg-slate-300"
72
+ role="separator"
73
+ />
74
+ <div
75
+ class="relative flex w-full p-3 flex-auto flex-col place-content-inherit align-items-inherit h-auto break-words text-left overflow-y-auto subpixel-antialiased"
76
+ >
77
+ <p
78
+ class="w-96"
79
+ >
80
+ Lorem commodo nulla adipisicing cillum Lorem sunt laboris exercitation esse. Exercitation ut cillum cupidatat deserunt occaecat pariatur laborum ut reprehenderit veniam. Culpa fugiat aliqua consectetur sit esse.
81
+ </p>
82
+ </div>
83
+ <hr
84
+ class="shrink-0 border-none w-full h-divider my-0 bg-slate-300"
85
+ role="separator"
86
+ />
87
+ <div
88
+ class="p-3 h-auto flex w-full items-center overflow-hidden color-inherit subpixel-antialiased rounded-b-small"
89
+ >
90
+ Footer
91
+ </div>
92
+ </div>
93
+ </div>
94
+ `;
@@ -0,0 +1,3 @@
1
+ import Card from './Card';
2
+
3
+ export default Card;
@@ -0,0 +1,7 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+
3
+ const sharedConfig = require('tailwind-config/tailwind.config');
4
+
5
+ module.exports = {
6
+ presets: [sharedConfig],
7
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "lib": ["DOM", "DOM.Iterable", "ESNext"]
5
+ },
6
+ "include": ["src", "globals.d.ts"]
7
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,15 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+ import { defineConfig } from 'tsup';
3
+
4
+ export default defineConfig({
5
+ clean: true,
6
+ dts: true,
7
+ entry: [
8
+ 'src',
9
+ '!src/**/__snapshots__/**',
10
+ '!src/**/*.stories.*',
11
+ '!src/**/*.test.*',
12
+ ],
13
+ format: ['cjs', 'esm'],
14
+ sourcemap: true,
15
+ });