@blenx-dev/core 0.1.0 → 0.2.3

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 (58) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/iconify.config.ts +23 -0
  3. package/package.json +17 -7
  4. package/scripts/generate-icons.ts +82 -0
  5. package/src/components/Accordion/accordion.tsx +2 -2
  6. package/src/components/Autocomplete/autocomplete.tsx +2 -2
  7. package/src/components/Breadcrumbs/breadcrumbs.tsx +3 -3
  8. package/src/components/Checkbox/checkbox.tsx +2 -15
  9. package/src/components/CloseButton/close-button.tsx +2 -5
  10. package/src/components/Combobox/combobox.tsx +5 -5
  11. package/src/components/Command/command.tsx +2 -2
  12. package/src/components/CopyButton/copy-button.tsx +3 -11
  13. package/src/components/Drawer/drawer.tsx +3 -29
  14. package/src/components/Icon/icon.tsx +3 -3
  15. package/src/components/Popover/popover.tsx +24 -14
  16. package/src/components/Select/select.tsx +5 -5
  17. package/src/components/Spinner/spinner.tsx +3 -5
  18. package/src/components/Stack/stack.tsx +5 -15
  19. package/src/components/Text/text.tsx +1 -1
  20. package/src/components/index.ts +0 -3
  21. package/src/icons/ArrowRightIcon.tsx +20 -0
  22. package/src/icons/CalendarIcon.tsx +20 -0
  23. package/src/icons/CheckIcon.tsx +20 -0
  24. package/src/icons/ChevronDownIcon.tsx +20 -0
  25. package/src/icons/ChevronLeftIcon.tsx +20 -0
  26. package/src/icons/ChevronRightIcon.tsx +20 -0
  27. package/src/icons/ChevronUpIcon.tsx +20 -0
  28. package/src/icons/CircleAlertIcon.tsx +20 -0
  29. package/src/icons/CopyIcon.tsx +20 -0
  30. package/src/icons/EllipsisIcon.tsx +20 -0
  31. package/src/icons/FolderOpenIcon.tsx +20 -0
  32. package/src/icons/ListIcon.tsx +20 -0
  33. package/src/icons/LoaderCircleIcon.tsx +20 -0
  34. package/src/icons/SearchIcon.tsx +20 -0
  35. package/src/icons/SquareCheckIcon.tsx +20 -0
  36. package/src/icons/XIcon.tsx +20 -0
  37. package/src/icons/index.ts +17 -0
  38. package/src/utils/sprinkles.css.ts +18 -4
  39. package/src/DataTable/data-table-column-toggle.tsx +0 -73
  40. package/src/DataTable/data-table-empty.tsx +0 -27
  41. package/src/DataTable/data-table-error.tsx +0 -25
  42. package/src/DataTable/data-table-infinite-loader.tsx +0 -73
  43. package/src/DataTable/data-table-loading.tsx +0 -67
  44. package/src/DataTable/data-table-pagination.tsx +0 -80
  45. package/src/DataTable/data-table-toolbar.tsx +0 -62
  46. package/src/DataTable/data-table.css.ts +0 -420
  47. package/src/DataTable/data-table.tsx +0 -507
  48. package/src/DataTable/index.ts +0 -24
  49. package/src/DataTable/types.ts +0 -169
  50. package/src/DataTable/use-infinite-scroll.ts +0 -67
  51. package/src/components/Calendar/calendar.css.ts +0 -187
  52. package/src/components/Calendar/calendar.tsx +0 -143
  53. package/src/components/Calendar/index.ts +0 -1
  54. package/src/components/ColorPicker/color-picker.tsx +0 -123
  55. package/src/components/ColorPicker/index.ts +0 -1
  56. package/src/components/DatePicker/date-picker.tsx +0 -75
  57. package/src/components/DatePicker/index.ts +0 -1
  58. package/src/components/Stack/stack.css.ts +0 -42
@@ -1,67 +0,0 @@
1
- import { type RefObject, useCallback, useEffect, useRef } from "react";
2
-
3
- interface UseInfiniteScrollOptions {
4
- /** Whether there are more pages to load */
5
- hasNextPage: boolean;
6
- /** Whether a fetch is currently in progress */
7
- isFetchingNextPage: boolean;
8
- /** Function to fetch the next page */
9
- fetchNextPage: () => void;
10
- /** IntersectionObserver root margin. Default: "200px" */
11
- rootMargin?: string;
12
- /** IntersectionObserver threshold. Default: 0 */
13
- threshold?: number;
14
- /** If true, the observer is disabled */
15
- enabled?: boolean;
16
- }
17
-
18
- interface UseInfiniteScrollReturn {
19
- /** Ref to attach to the sentinel element */
20
- sentinelRef: RefObject<HTMLDivElement | null>;
21
- }
22
-
23
- /**
24
- * Hook to observe a sentinel element and trigger fetchNextPage when visible.
25
- *
26
- * Uses IntersectionObserver with a configurable root margin and threshold.
27
- * Respects hasNextPage, isFetchingNextPage, and enabled state to prevent
28
- * duplicate requests.
29
- */
30
- export function useInfiniteScroll({
31
- hasNextPage,
32
- isFetchingNextPage,
33
- fetchNextPage,
34
- rootMargin = "200px",
35
- threshold = 0,
36
- enabled = true,
37
- }: UseInfiniteScrollOptions): UseInfiniteScrollReturn {
38
- const sentinelRef = useRef<HTMLDivElement | null>(null);
39
-
40
- const handleIntersection = useCallback(
41
- (entries: IntersectionObserverEntry[]) => {
42
- const [entry] = entries;
43
- if (entry?.isIntersecting && hasNextPage && !isFetchingNextPage) {
44
- fetchNextPage();
45
- }
46
- },
47
- [hasNextPage, isFetchingNextPage, fetchNextPage],
48
- );
49
-
50
- useEffect(() => {
51
- const sentinel = sentinelRef.current;
52
- if (!sentinel || !enabled) return;
53
-
54
- const observer = new IntersectionObserver(handleIntersection, {
55
- rootMargin,
56
- threshold,
57
- });
58
-
59
- observer.observe(sentinel);
60
-
61
- return () => {
62
- observer.disconnect();
63
- };
64
- }, [enabled, handleIntersection, rootMargin, threshold]);
65
-
66
- return { sentinelRef };
67
- }
@@ -1,187 +0,0 @@
1
- import { style } from "@vanilla-extract/css";
2
- import { semanticVars, tokenVars } from "@blenx-dev/theme/contract";
3
-
4
- export const root = style({
5
- display: "inline-block",
6
- width: "100%",
7
- });
8
-
9
- export const months = style({
10
- display: "flex",
11
- flexDirection: "column",
12
- });
13
-
14
- export const monthCaption = style({
15
- display: "flex",
16
- alignItems: "center",
17
- justifyContent: "center",
18
- position: "relative",
19
- paddingBottom: tokenVars.spacing.sm,
20
- minHeight: 40,
21
- });
22
-
23
- export const captionLabel = style({
24
- fontSize: tokenVars.fontSize.md,
25
- fontWeight: tokenVars.fontWeight.semibold,
26
- color: semanticVars.text.primary,
27
- margin: 0,
28
- padding: 0,
29
- });
30
-
31
- export const nav = style({
32
- height: "fit-content",
33
- display: "flex",
34
- justifyContent: "space-between",
35
- alignItems: "center",
36
- position: "absolute",
37
- inset: 0,
38
- padding: tokenVars.spacing.xs,
39
- pointerEvents: "none",
40
- zIndex: 10,
41
- });
42
-
43
- export const buttonNav = style({
44
- pointerEvents: "auto",
45
- appearance: "none",
46
- backgroundColor: "transparent",
47
- border: "none",
48
- padding: 0,
49
- font: "inherit",
50
- WebkitTapHighlightColor: "transparent",
51
- width: 28,
52
- height: 28,
53
- borderRadius: tokenVars.borderRadius.default,
54
- display: "flex",
55
- alignItems: "center",
56
- justifyContent: "center",
57
- color: semanticVars.text.secondary,
58
- cursor: "pointer",
59
- flexShrink: 0,
60
- selectors: {
61
- "&:hover": {
62
- backgroundColor: semanticVars.background.subtle,
63
- },
64
- "&:focus-visible": {
65
- boxShadow: `0 0 0 2px ${semanticVars.focus.ring}`,
66
- },
67
- },
68
- });
69
-
70
- export const monthGrid = style({
71
- width: "100%",
72
- borderCollapse: "collapse",
73
- });
74
-
75
- export const weekday = style({
76
- fontSize: tokenVars.fontSize.xs,
77
- fontWeight: tokenVars.fontWeight.medium,
78
- color: semanticVars.text.secondary,
79
- textAlign: "center",
80
- verticalAlign: "middle",
81
- padding: tokenVars.spacing.xxs,
82
- width: 40,
83
- height: 32,
84
- boxSizing: "border-box",
85
- });
86
-
87
- export const day = style({
88
- textAlign: "center",
89
- verticalAlign: "middle",
90
- padding: 1,
91
- width: 40,
92
- height: 40,
93
- boxSizing: "border-box",
94
- });
95
-
96
- export const dayButton = style({
97
- display: "flex",
98
- alignItems: "center",
99
- justifyContent: "center",
100
- width: "100%",
101
- height: "100%",
102
- minWidth: 32,
103
- minHeight: 32,
104
- maxWidth: 36,
105
- maxHeight: 36,
106
- borderRadius: tokenVars.borderRadius.default,
107
- border: "none",
108
- backgroundColor: "transparent",
109
- cursor: "pointer",
110
- fontSize: tokenVars.fontSize.sm,
111
- color: semanticVars.text.primary,
112
- outline: "none",
113
- margin: "0 auto",
114
- padding: 0,
115
- fontFamily: "inherit",
116
- lineHeight: 1,
117
- transitionProperty: "background-color, color",
118
- transitionDuration: "100ms",
119
- transitionTimingFunction: "ease",
120
- selectors: {
121
- "&:hover": {
122
- backgroundColor: semanticVars.background.subtle,
123
- },
124
- "&:focus-visible": {
125
- boxShadow: `0 0 0 2px ${semanticVars.focus.ring}`,
126
- },
127
- },
128
- });
129
-
130
- export const dayButtonSelected = style({
131
- backgroundColor: semanticVars.interactive.primary.default,
132
- color: semanticVars.interactive.primaryFg,
133
- fontWeight: tokenVars.fontWeight.semibold,
134
- selectors: {
135
- "&:hover": {
136
- backgroundColor: semanticVars.interactive.primary.default,
137
- },
138
- },
139
- });
140
-
141
- export const dayButtonToday = style({
142
- fontWeight: tokenVars.fontWeight.semibold,
143
- });
144
-
145
- export const dayButtonDisabled = style({
146
- color: semanticVars.text.disabled,
147
- cursor: "not-allowed",
148
- opacity: 0.5,
149
- selectors: {
150
- "&:hover": {
151
- backgroundColor: "transparent",
152
- },
153
- },
154
- });
155
-
156
- export const dayButtonOutside = style({
157
- color: semanticVars.text.disabled,
158
- opacity: 0.5,
159
- });
160
-
161
- export const footer = style({
162
- paddingTop: tokenVars.spacing.sm,
163
- });
164
-
165
- export const dropdowns = style({
166
- display: "flex",
167
- gap: tokenVars.spacing.xs,
168
- alignItems: "center",
169
- });
170
-
171
- export const dropdown = style({
172
- appearance: "none",
173
- backgroundColor: semanticVars.background.subtle,
174
- border: `1px solid ${semanticVars.border.default}`,
175
- borderRadius: tokenVars.borderRadius.default,
176
- color: semanticVars.text.primary,
177
- fontSize: tokenVars.fontSize.sm,
178
- padding: `${tokenVars.spacing.xxs} ${tokenVars.spacing.sm}`,
179
- cursor: "pointer",
180
- outline: "none",
181
- selectors: {
182
- "&:focus-visible": {
183
- borderColor: semanticVars.border.strong,
184
- boxShadow: `0 0 0 2px ${semanticVars.border.strong}`,
185
- },
186
- },
187
- });
@@ -1,143 +0,0 @@
1
- "use client";
2
-
3
- import { CaretLeftIcon, CaretRightIcon, CaretUpDownIcon } from "@phosphor-icons/react";
4
- import clsx from "clsx";
5
- import { useEffect, useRef } from "react";
6
- import { DayPicker, type DayPickerProps } from "react-day-picker";
7
- import { Button } from "../Button/button";
8
- import {
9
- root,
10
- months,
11
- monthCaption,
12
- captionLabel,
13
- nav,
14
- buttonNav,
15
- monthGrid,
16
- weekday,
17
- day,
18
- dayButton,
19
- dayButtonSelected,
20
- dayButtonToday,
21
- dayButtonDisabled,
22
- dayButtonOutside,
23
- footer,
24
- dropdowns,
25
- dropdown,
26
- } from "./calendar.css";
27
-
28
- function StyledDayButton({
29
- day: _day,
30
- modifiers,
31
- className,
32
- ...props
33
- }: {
34
- day: object;
35
- modifiers: Record<string, boolean>;
36
- } & React.ComponentPropsWithoutRef<"button">) {
37
- const ref = useRef<HTMLButtonElement>(null);
38
-
39
- useEffect(() => {
40
- if (modifiers.focused) {
41
- ref.current?.focus();
42
- }
43
- }, [modifiers.focused]);
44
-
45
- const isSelected = modifiers.selected;
46
- const isToday = modifiers.today;
47
- const isDisabled = modifiers.disabled;
48
- const isOutside = modifiers.outside;
49
-
50
- const composed = clsx(
51
- dayButton,
52
- isSelected && dayButtonSelected,
53
- isToday && dayButtonToday,
54
- isDisabled && dayButtonDisabled,
55
- isOutside && dayButtonOutside,
56
- className,
57
- );
58
-
59
- return <button ref={ref} className={composed} {...props} />;
60
- }
61
-
62
- function Calendar({ className, components: userComponents, ...props }: DayPickerProps) {
63
- const classNames = {
64
- root: clsx(root, className),
65
- months: months,
66
- month: "",
67
- month_caption: monthCaption,
68
- caption_label: captionLabel,
69
- nav: nav,
70
- button_previous: buttonNav,
71
- button_next: buttonNav,
72
- chevron: "",
73
- month_grid: monthGrid,
74
- weekdays: "",
75
- weekday: weekday,
76
- weeks: "",
77
- week: "",
78
- day: day,
79
- day_button: "",
80
- footer: footer,
81
- dropdowns: dropdowns,
82
- dropdown: dropdown,
83
- dropdown_root: "",
84
- months_dropdown: "",
85
- years_dropdown: "",
86
- week_number: "",
87
- week_number_header: "",
88
- selected: "",
89
- today: "",
90
- disabled: "",
91
- hidden: "",
92
- outside: "",
93
- focused: "",
94
- range_start: "",
95
- range_end: "",
96
- range_middle: "",
97
- weeks_before_enter: "",
98
- weeks_before_exit: "",
99
- weeks_after_enter: "",
100
- weeks_after_exit: "",
101
- caption_after_enter: "",
102
- caption_after_exit: "",
103
- caption_before_enter: "",
104
- caption_before_exit: "",
105
- };
106
- const defaultComponents = {
107
- Chevron: ({
108
- orientation,
109
- ...chevronProps
110
- }: {
111
- orientation?: "left" | "right" | "up" | "down";
112
- }): React.ReactElement => {
113
- if (orientation === "left") {
114
- return (
115
- <Button size="icon" variant="ghost">
116
- <CaretLeftIcon {...chevronProps} aria-hidden="true" />
117
- </Button>
118
- );
119
- }
120
- if (orientation === "right") {
121
- return (
122
- <Button size="icon" variant="ghost">
123
- <CaretRightIcon {...chevronProps} aria-hidden="true" />
124
- </Button>
125
- );
126
- }
127
- return (
128
- <Button size="icon" variant="ghost">
129
- <CaretUpDownIcon {...chevronProps} aria-hidden="true" />
130
- </Button>
131
- );
132
- },
133
- };
134
- const mergedComponents = {
135
- ...defaultComponents,
136
- ...userComponents,
137
- DayButton: StyledDayButton,
138
- };
139
-
140
- return <DayPicker classNames={classNames} components={mergedComponents} {...props} />;
141
- }
142
-
143
- export { Calendar };
@@ -1 +0,0 @@
1
- export * from "./calendar";
@@ -1,123 +0,0 @@
1
- import { CaretDownIcon } from "@phosphor-icons/react";
2
- import { useCallback, useEffect, useRef, useState } from "react";
3
- import { HexAlphaColorPicker, HexColorInput } from "react-colorful";
4
- import { Button } from "../Button/button";
5
- import { ColorSwatch } from "../ColorSwatch/color-swatch";
6
- import { Field, FieldLabel } from "../Field/field";
7
- import { Input } from "../Input/input";
8
- import {
9
- Popover,
10
- PopoverArrow,
11
- PopoverCompound,
12
- PopoverPopup,
13
- PopoverPositioner,
14
- PopoverTrigger,
15
- } from "../Popover/popover";
16
- import { VStack } from "../Stack/stack";
17
- import { Text } from "../Text/text";
18
-
19
- const HEX_REGEX = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
20
-
21
- type Props = {
22
- value: string;
23
- onChange: (color: string) => void;
24
- label?: string;
25
- };
26
-
27
- export function ColorPicker({ value, onChange, label }: Props) {
28
- const [open, setOpen] = useState(false);
29
- const [internalValue, setInternalValue] = useState(value);
30
- const lastValidRef = useRef(value);
31
- const triggerRef = useRef<HTMLButtonElement>(null);
32
- const prevValueRef = useRef(value);
33
-
34
- useEffect(() => {
35
- if (prevValueRef.current !== value) {
36
- prevValueRef.current = value;
37
- setInternalValue(value);
38
- lastValidRef.current = value;
39
- }
40
- }, [value]);
41
-
42
- const handleOpenChange = useCallback((nextOpen: boolean) => {
43
- if (!nextOpen && triggerRef.current) {
44
- triggerRef.current.focus();
45
- }
46
- setOpen(nextOpen);
47
- }, []);
48
-
49
- const handleHexInputChange = useCallback(
50
- (newColor: string) => {
51
- setInternalValue(newColor);
52
- if (HEX_REGEX.test(newColor)) {
53
- lastValidRef.current = newColor;
54
- onChange(newColor);
55
- }
56
- },
57
- [onChange],
58
- );
59
-
60
- const revertToLastValid = useCallback(() => {
61
- setInternalValue(lastValidRef.current);
62
- onChange(lastValidRef.current);
63
- }, [onChange]);
64
-
65
- return (
66
- <Field>
67
- <Popover open={open} onOpenChange={handleOpenChange}>
68
- {label && (
69
- <FieldLabel
70
- onClick={() => {
71
- triggerRef.current?.click();
72
- triggerRef.current?.focus();
73
- }}
74
- >
75
- {label}
76
- </FieldLabel>
77
- )}
78
- <PopoverTrigger
79
- ref={triggerRef}
80
- aria-label={label ? `${label} color picker` : "Color picker"}
81
- render={<Button type="button" variant="outline" />}
82
- >
83
- <ColorSwatch color={value} size={20} />
84
- <Text variant="body2">{value}</Text>
85
- <CaretDownIcon />
86
- </PopoverTrigger>
87
- <PopoverCompound.Portal>
88
- <PopoverPositioner sideOffset={6} side="bottom" align="start">
89
- <PopoverPopup>
90
- <PopoverArrow />
91
- <VStack gap="xs">
92
- <HexAlphaColorPicker
93
- color={internalValue}
94
- onChange={(color) => {
95
- setInternalValue(color);
96
- lastValidRef.current = color;
97
- onChange(color);
98
- }}
99
- />
100
- <Input
101
- size="sm"
102
- render={
103
- <HexColorInput
104
- color={internalValue}
105
- onChange={handleHexInputChange}
106
- onBlur={() => {
107
- if (!HEX_REGEX.test(internalValue)) {
108
- revertToLastValid();
109
- }
110
- }}
111
- prefixed
112
- aria-label="Hex color value"
113
- />
114
- }
115
- />
116
- </VStack>
117
- </PopoverPopup>
118
- </PopoverPositioner>
119
- </PopoverCompound.Portal>
120
- </Popover>
121
- </Field>
122
- );
123
- }
@@ -1 +0,0 @@
1
- export * from "./color-picker";
@@ -1,75 +0,0 @@
1
- "use client";
2
-
3
- import { CalendarBlankIcon } from "@phosphor-icons/react";
4
- import { useRef, useState } from "react";
5
- import { Button } from "../Button/button";
6
- import { Calendar } from "../Calendar/calendar";
7
- import { Field, FieldError, FieldLabel } from "../Field/field";
8
- import {
9
- Popover,
10
- PopoverCompound,
11
- PopoverPopup,
12
- PopoverPositioner,
13
- PopoverTrigger,
14
- } from "../Popover/popover";
15
-
16
- type Props = {
17
- value: Date | undefined;
18
- onChange: (date: Date | undefined) => void;
19
- format?: (date: Date | undefined) => string;
20
- label?: string;
21
- placeholder?: string;
22
- error?: string;
23
- disabled?: boolean;
24
- };
25
-
26
- export function DatePicker({
27
- value,
28
- onChange,
29
- label,
30
- format,
31
- placeholder = "Pick a date",
32
- error,
33
- disabled,
34
- }: Props) {
35
- const [open, setOpen] = useState(false);
36
- const triggerRef = useRef<HTMLButtonElement>(null);
37
-
38
- const handleSelect = (date: Date | undefined) => {
39
- onChange(date);
40
- setOpen(false);
41
- };
42
-
43
- return (
44
- <Field>
45
- <Popover open={open} onOpenChange={setOpen}>
46
- {label && (
47
- <FieldLabel
48
- onClick={() => {
49
- triggerRef.current?.click();
50
- triggerRef.current?.focus();
51
- }}
52
- >
53
- {label}
54
- </FieldLabel>
55
- )}
56
- <PopoverTrigger
57
- ref={triggerRef}
58
- disabled={disabled}
59
- render={<Button type="button" variant="outline" fullWidth />}
60
- >
61
- <CalendarBlankIcon size={18} />
62
- {value ? (format?.(value) ?? value.toString()) : placeholder}
63
- </PopoverTrigger>
64
- <PopoverCompound.Portal>
65
- <PopoverPositioner sideOffset={6} side="bottom" align="start">
66
- <PopoverPopup>
67
- <Calendar mode="single" selected={value} onSelect={handleSelect} />
68
- </PopoverPopup>
69
- </PopoverPositioner>
70
- </PopoverCompound.Portal>
71
- </Popover>
72
- {error && <FieldError>{error}</FieldError>}
73
- </Field>
74
- );
75
- }
@@ -1 +0,0 @@
1
- export * from "./date-picker";
@@ -1,42 +0,0 @@
1
- import { recipe } from "@vanilla-extract/recipes";
2
- import { tokenVars } from "@blenx-dev/theme/contract";
3
-
4
- export const stackRecipe = recipe({
5
- variants: {
6
- gap: {
7
- none: { gap: 0 },
8
- xxs: { gap: tokenVars.spacing.xxs },
9
- xs: { gap: tokenVars.spacing.xs },
10
- sm: { gap: tokenVars.spacing.sm },
11
- md: { gap: tokenVars.spacing.md },
12
- lg: { gap: tokenVars.spacing.lg },
13
- xl: { gap: tokenVars.spacing.xl },
14
- xxl: { gap: tokenVars.spacing.xxl },
15
- xxxl: { gap: tokenVars.spacing.xxxl },
16
- huge: { gap: tokenVars.spacing.huge },
17
- massive: { gap: tokenVars.spacing.massive },
18
- },
19
- align: {
20
- start: { alignItems: "flex-start" },
21
- center: { alignItems: "center" },
22
- end: { alignItems: "flex-end" },
23
- stretch: { alignItems: "stretch" },
24
- baseline: { alignItems: "baseline" },
25
- },
26
- justify: {
27
- start: { justifyContent: "flex-start" },
28
- center: { justifyContent: "center" },
29
- end: { justifyContent: "flex-end" },
30
- between: { justifyContent: "space-between" },
31
- around: { justifyContent: "space-around" },
32
- evenly: { justifyContent: "space-evenly" },
33
- stretch: { justifyContent: "stretch" },
34
- },
35
- wrap: {
36
- true: { flexWrap: "wrap" },
37
- },
38
- },
39
- defaultVariants: {
40
- gap: "md",
41
- },
42
- });