doom-design-system 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/components/A2UI/catalog.js +98 -0
  2. package/dist/components/A2UI/mapping.js +5 -0
  3. package/dist/components/Checkbox/Checkbox.d.ts +1 -0
  4. package/dist/components/Checkbox/Checkbox.js +20 -4
  5. package/dist/components/FileUpload/FileUpload.js +2 -1
  6. package/dist/components/Page/Page.module.css +9 -3
  7. package/dist/components/Popover/Popover.d.ts +1 -1
  8. package/dist/components/Popover/Popover.js +53 -23
  9. package/dist/components/Rating/Rating.d.ts +17 -0
  10. package/dist/components/Rating/Rating.js +126 -0
  11. package/dist/components/Rating/Rating.module.css +131 -0
  12. package/dist/components/Rating/index.d.ts +1 -0
  13. package/dist/components/Rating/index.js +1 -0
  14. package/dist/components/Table/Table.d.ts +2 -3
  15. package/dist/components/Table/Table.js +2 -20
  16. package/dist/components/ToggleGroup/ToggleGroup.d.ts +22 -0
  17. package/dist/components/ToggleGroup/ToggleGroup.js +157 -0
  18. package/dist/components/ToggleGroup/ToggleGroup.module.css +81 -0
  19. package/dist/components/ToggleGroup/index.d.ts +1 -0
  20. package/dist/components/ToggleGroup/index.js +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +2 -0
  23. package/dist/lib/filter/ast/array-filter.d.ts +7 -0
  24. package/dist/lib/filter/ast/array-filter.js +16 -0
  25. package/dist/lib/filter/ast/evaluate.d.ts +6 -0
  26. package/dist/lib/filter/ast/evaluate.js +35 -0
  27. package/dist/lib/filter/ast/index.d.ts +5 -0
  28. package/dist/lib/filter/ast/index.js +4 -0
  29. package/dist/lib/filter/ast/operators.d.ts +7 -0
  30. package/dist/{components/Table/utils/filterAst.js → lib/filter/ast/operators.js} +0 -52
  31. package/dist/lib/filter/ast/simple.d.ts +7 -0
  32. package/dist/lib/filter/ast/simple.js +26 -0
  33. package/dist/lib/filter/ast/types.d.ts +24 -0
  34. package/dist/lib/filter/ast/types.js +1 -0
  35. package/dist/lib/filter/index.d.ts +7 -0
  36. package/dist/lib/filter/index.js +7 -0
  37. package/dist/lib/filter/ui/FilterBuilder.d.ts +25 -0
  38. package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterBuilder.js +3 -3
  39. package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterConditionRow.d.ts +1 -1
  40. package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterConditionRow.js +4 -4
  41. package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterGroup.d.ts +9 -9
  42. package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterGroup.js +7 -7
  43. package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterSheetNested.d.ts +3 -3
  44. package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterSheetNested.js +4 -4
  45. package/dist/lib/filter/ui/convert.d.ts +16 -0
  46. package/dist/lib/filter/ui/convert.js +60 -0
  47. package/dist/lib/filter/ui/index.d.ts +5 -0
  48. package/dist/lib/filter/ui/index.js +5 -0
  49. package/dist/lib/filter/ui/utils/tree-utils.d.ts +15 -0
  50. package/dist/styles/globals.css +1 -1
  51. package/dist/tsconfig.build.tsbuildinfo +1 -1
  52. package/dist/vitest.config.js +6 -1
  53. package/package.json +10 -3
  54. package/dist/components/Table/FilterBuilder/FilterBuilder.d.ts +0 -20
  55. package/dist/components/Table/FilterBuilder/utils/tree-utils.d.ts +0 -15
  56. package/dist/components/Table/utils/arrayFilter.d.ts +0 -7
  57. package/dist/components/Table/utils/arrayFilter.js +0 -21
  58. package/dist/components/Table/utils/filterAst.d.ts +0 -33
  59. /package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterBuilder.module.css +0 -0
  60. /package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterConditionRow.module.css +0 -0
  61. /package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterGroup.module.css +0 -0
  62. /package/dist/{components/Table/FilterBuilder → lib/filter/ui}/FilterSheet.module.css +0 -0
  63. /package/dist/{components/Table/FilterBuilder → lib/filter/ui}/utils/tree-utils.js +0 -0
@@ -408,6 +408,104 @@ export const componentCatalog = [
408
408
  { name: "disabled", type: "boolean", description: "Disable slider" },
409
409
  ],
410
410
  },
411
+ {
412
+ type: "rating",
413
+ name: "Rating",
414
+ category: "primitives",
415
+ description: "Icon-based rating with half-value support",
416
+ props: [
417
+ {
418
+ name: "value",
419
+ type: "number",
420
+ description: "Controlled rating value",
421
+ },
422
+ {
423
+ name: "defaultValue",
424
+ type: "number",
425
+ description: "Uncontrolled initial value",
426
+ },
427
+ {
428
+ name: "count",
429
+ type: "number",
430
+ description: "Number of icons",
431
+ default: "5",
432
+ },
433
+ {
434
+ name: "allowHalf",
435
+ type: "boolean",
436
+ description: "Enable half-value selection",
437
+ },
438
+ {
439
+ name: "size",
440
+ type: "'sm' | 'md' | 'lg'",
441
+ description: "Icon size",
442
+ default: "md",
443
+ },
444
+ {
445
+ name: "readOnly",
446
+ type: "boolean",
447
+ description: "Read-only display mode",
448
+ },
449
+ { name: "disabled", type: "boolean", description: "Disable rating" },
450
+ ],
451
+ },
452
+ {
453
+ type: "toggle-group",
454
+ name: "ToggleGroup",
455
+ category: "primitives",
456
+ description: "Grouped toggle buttons with single or multi select",
457
+ props: [
458
+ {
459
+ name: "type",
460
+ type: "'single' | 'multiple'",
461
+ required: true,
462
+ description: "Selection mode",
463
+ },
464
+ {
465
+ name: "value",
466
+ type: "string | string[]",
467
+ description: "Controlled value",
468
+ },
469
+ {
470
+ name: "defaultValue",
471
+ type: "string | string[]",
472
+ description: "Default value (uncontrolled)",
473
+ },
474
+ {
475
+ name: "size",
476
+ type: "'sm' | 'md' | 'lg'",
477
+ description: "Control size",
478
+ default: "md",
479
+ },
480
+ { name: "disabled", type: "boolean", description: "Disable all items" },
481
+ {
482
+ name: "children",
483
+ type: "A2UIChildRef",
484
+ description: "toggle-group-item children",
485
+ },
486
+ ],
487
+ },
488
+ {
489
+ type: "toggle-group-item",
490
+ name: "ToggleGroupItem",
491
+ category: "primitives",
492
+ description: "Single toggle option within a ToggleGroup",
493
+ usesTextProp: true,
494
+ props: [
495
+ {
496
+ name: "value",
497
+ type: "string",
498
+ required: true,
499
+ description: "Item value",
500
+ },
501
+ {
502
+ name: "text",
503
+ type: "A2UITextValue",
504
+ description: "Label text",
505
+ },
506
+ { name: "disabled", type: "boolean", description: "Disable this item" },
507
+ ],
508
+ },
411
509
  {
412
510
  type: "link",
413
511
  name: "Link",
@@ -22,6 +22,7 @@ import { Link } from "../Link/Link.js";
22
22
  // Feedback - Static display
23
23
  import { ProgressBar } from "../ProgressBar/ProgressBar.js";
24
24
  import { RadioGroup, RadioGroupItem } from "../RadioGroup/RadioGroup.js";
25
+ import { Rating } from "../Rating/Rating.js";
25
26
  import { Select } from "../Select/Select.js";
26
27
  import { Sidebar, SidebarFooter, SidebarGroup, SidebarHeader, SidebarItem, SidebarMobileTrigger, SidebarNav, SidebarSection, } from "../Sidebar/Sidebar.js";
27
28
  import { Skeleton } from "../Skeleton/Skeleton.js";
@@ -34,6 +35,7 @@ import { Table } from "../Table/Table.js";
34
35
  import { Tabs, TabsBody, TabsContent, TabsList, TabsTrigger, } from "../Tabs/Tabs.js";
35
36
  import { Text } from "../Text/Text.js";
36
37
  import { Textarea } from "../Textarea/Textarea.js";
38
+ import { ToggleGroup, ToggleGroupItem } from "../ToggleGroup/ToggleGroup.js";
37
39
  import { Tooltip } from "../Tooltip/Tooltip.js";
38
40
  // Wrappers (for components needing JSON-to-function adaptation)
39
41
  import { ChartWrapper } from "./wrappers/index.js";
@@ -52,11 +54,14 @@ export const componentMap = {
52
54
  link: Link,
53
55
  "radio-group": RadioGroup,
54
56
  "radio-group-item": RadioGroupItem,
57
+ rating: Rating,
55
58
  slider: Slider,
56
59
  spinner: Spinner,
57
60
  switch: Switch,
58
61
  text: Text,
59
62
  textarea: Textarea,
63
+ "toggle-group": ToggleGroup,
64
+ "toggle-group-item": ToggleGroupItem,
60
65
  tooltip: Tooltip,
61
66
  // Layout
62
67
  box: "div",
@@ -2,5 +2,6 @@ import React from "react";
2
2
  export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
3
3
  label?: string;
4
4
  error?: boolean;
5
+ indeterminate?: boolean;
5
6
  }
6
7
  export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>>;
@@ -12,14 +12,30 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  };
13
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  import clsx from "clsx";
15
- import { Check } from "lucide-react";
16
- import { forwardRef, useId } from "react";
15
+ import { Check, Minus } from "lucide-react";
16
+ import { forwardRef, useCallback, useEffect, useId, useRef, } from "react";
17
17
  import { Label } from "../Label/index.js";
18
18
  import styles from "./Checkbox.module.css";
19
19
  export const Checkbox = forwardRef((_a, ref) => {
20
- var { className, label, error, disabled, checked, defaultChecked, onChange, id: propsId } = _a, props = __rest(_a, ["className", "label", "error", "disabled", "checked", "defaultChecked", "onChange", "id"]);
20
+ var { className, label, error, disabled, checked, defaultChecked, indeterminate, onChange, id: propsId } = _a, props = __rest(_a, ["className", "label", "error", "disabled", "checked", "defaultChecked", "indeterminate", "onChange", "id"]);
21
21
  const generatedId = useId();
22
22
  const id = propsId || generatedId;
23
- return (_jsxs(Label, { className: clsx(styles.checkboxWrapper, disabled && styles.disabled, className), htmlFor: id, children: [_jsx("input", Object.assign({ ref: ref, checked: checked, className: styles.checkboxInput, defaultChecked: defaultChecked, disabled: disabled, id: id, type: "checkbox", onChange: onChange }, props)), _jsx("span", { "aria-hidden": "true", className: clsx(styles.checkboxDisplay), children: _jsx(Check, { className: styles.icon }) }), label && _jsx("span", { className: styles.labelOverride, children: label })] }));
23
+ const internalRef = useRef(null);
24
+ const mergedRef = useCallback((node) => {
25
+ internalRef.current = node;
26
+ if (typeof ref === "function") {
27
+ ref(node);
28
+ }
29
+ else if (ref) {
30
+ ref.current =
31
+ node;
32
+ }
33
+ }, [ref]);
34
+ useEffect(() => {
35
+ if (internalRef.current) {
36
+ internalRef.current.indeterminate = !!(indeterminate && !checked);
37
+ }
38
+ }, [indeterminate, checked]);
39
+ return (_jsxs(Label, { className: clsx(styles.checkboxWrapper, disabled && styles.disabled, className), htmlFor: id, children: [_jsx("input", Object.assign({ ref: mergedRef, checked: checked, className: styles.checkboxInput, defaultChecked: defaultChecked, disabled: disabled, id: id, type: "checkbox", onChange: onChange }, props)), _jsx("span", { "aria-hidden": "true", className: clsx(styles.checkboxDisplay), children: indeterminate && !checked ? (_jsx(Minus, { className: styles.icon, "data-testid": "minus-icon" })) : (_jsx(Check, { className: styles.icon })) }), label && _jsx("span", { className: styles.labelOverride, children: label })] }));
24
40
  });
25
41
  Checkbox.displayName = "Checkbox";
@@ -92,8 +92,9 @@ export const FileUpload = ({ label, helperText, accept, maxSize, multiple = fals
92
92
  }
93
93
  }, [showPreview]);
94
94
  const isFileAccepted = (file) => {
95
- if (!accept)
95
+ if (!accept) {
96
96
  return true;
97
+ }
97
98
  const acceptedTypes = accept.split(",").map((t) => t.trim().toLowerCase());
98
99
  const fileName = file.name.toLowerCase();
99
100
  const mimeType = file.type.toLowerCase();
@@ -4,16 +4,22 @@
4
4
  width: 100%;
5
5
  }
6
6
  .default {
7
+ display: flex;
8
+ flex-direction: column;
7
9
  width: 90%;
8
10
  max-width: var(--page-max-width);
9
- backdrop-filter: blur(var(--blur-standard));
10
11
  margin: 0 auto;
11
- padding: var(--space-8) var(--space-4);
12
+ padding: 0 var(--space-4) var(--space-4);
13
+ }
14
+ .default > *:first-child {
15
+ min-height: var(--header-height);
16
+ display: flex;
17
+ align-items: center;
18
+ flex-shrink: 0;
12
19
  }
13
20
  @media (max-width: 1024px) {
14
21
  .default {
15
22
  width: 95%;
16
- padding: var(--space-4);
17
23
  }
18
24
  }
19
25
  .fullWidth {
@@ -4,7 +4,7 @@ interface PopoverProps {
4
4
  content: React.ReactNode;
5
5
  isOpen: boolean;
6
6
  onClose: () => void;
7
- placement?: "bottom-start" | "bottom-end" | "bottom-center" | "top-start" | "top-end" | "top-center";
7
+ placement?: "bottom-start" | "bottom-end" | "bottom-center" | "top-start" | "top-end" | "top-center" | "right-start" | "right-end" | "right-center" | "left-start" | "left-end" | "left-center";
8
8
  offset?: number;
9
9
  }
10
10
  export declare function Popover({ trigger, content, isOpen, onClose, placement, offset, }: PopoverProps): import("react/jsx-runtime").JSX.Element;
@@ -19,10 +19,10 @@ export function Popover({ trigger, content, isOpen, onClose, placement = "bottom
19
19
  let top = 0;
20
20
  let left = 0;
21
21
  let origin = "top center";
22
- // Edge Config
23
22
  const padding = 16;
24
- const isTop = placement.startsWith("top");
25
- if (isTop) {
23
+ const side = placement.split("-")[0];
24
+ const align = placement.split("-")[1];
25
+ if (side === "top") {
26
26
  top = triggerRect.top - contentRect.height - offset;
27
27
  origin = "bottom";
28
28
  if (top < 0) {
@@ -30,7 +30,7 @@ export function Popover({ trigger, content, isOpen, onClose, placement = "bottom
30
30
  origin = "top";
31
31
  }
32
32
  }
33
- else {
33
+ else if (side === "bottom") {
34
34
  top = triggerRect.bottom + offset;
35
35
  origin = "top";
36
36
  if (top + contentRect.height > viewportHeight) {
@@ -41,37 +41,68 @@ export function Popover({ trigger, content, isOpen, onClose, placement = "bottom
41
41
  }
42
42
  }
43
43
  }
44
- // Vertical Clamping (Fail-safe)
44
+ else if (side === "right") {
45
+ left = triggerRect.right + offset;
46
+ origin = "left";
47
+ if (left + contentRect.width > viewportWidth) {
48
+ left = triggerRect.left - contentRect.width - offset;
49
+ origin = "right";
50
+ if (left < 0) {
51
+ left = padding;
52
+ }
53
+ }
54
+ }
55
+ else if (side === "left") {
56
+ left = triggerRect.left - contentRect.width - offset;
57
+ origin = "right";
58
+ if (left < 0) {
59
+ left = triggerRect.right + offset;
60
+ origin = "left";
61
+ }
62
+ }
63
+ if (side === "top" || side === "bottom") {
64
+ if (align === "start") {
65
+ left = triggerRect.left;
66
+ origin += " left";
67
+ }
68
+ else if (align === "end") {
69
+ left = triggerRect.right - contentRect.width;
70
+ origin += " right";
71
+ }
72
+ else {
73
+ left = triggerRect.left + triggerRect.width / 2 - contentRect.width / 2;
74
+ origin += " center";
75
+ }
76
+ }
77
+ else {
78
+ if (align === "start") {
79
+ top = triggerRect.top;
80
+ origin = "top " + origin;
81
+ }
82
+ else if (align === "end") {
83
+ top = triggerRect.bottom - contentRect.height;
84
+ origin = "bottom " + origin;
85
+ }
86
+ else {
87
+ top = triggerRect.top + triggerRect.height / 2 - contentRect.height / 2;
88
+ origin = "center " + origin;
89
+ }
90
+ }
45
91
  if (top < padding) {
46
92
  top = padding;
47
93
  }
48
94
  if (top + contentRect.height > viewportHeight - padding) {
49
95
  top = viewportHeight - contentRect.height - padding;
50
96
  }
51
- const align = placement.split("-")[1];
52
- if (align === "start") {
53
- left = triggerRect.left;
54
- origin += " left";
55
- }
56
- else if (align === "end") {
57
- left = triggerRect.right - contentRect.width;
58
- origin += " right";
59
- }
60
- else {
61
- left = triggerRect.left + triggerRect.width / 2 - contentRect.width / 2;
62
- origin += " center";
97
+ if (left < padding) {
98
+ left = padding;
63
99
  }
64
- // Horizontal Clamping
65
100
  if (left + contentRect.width > viewportWidth - padding) {
66
101
  left = viewportWidth - contentRect.width - padding;
67
102
  }
68
- if (left < padding) {
69
- left = padding;
70
- }
71
103
  setPosition({ top, left });
72
104
  setTransformOrigin(origin);
73
105
  }, [isOpen, placement, offset]);
74
- // Use useLayoutEffect for layout measurements to prevent flash
75
106
  useLayoutEffect(() => {
76
107
  if (isOpen) {
77
108
  updatePosition();
@@ -83,7 +114,6 @@ export function Popover({ trigger, content, isOpen, onClose, placement = "bottom
83
114
  window.removeEventListener("scroll", updatePosition, true);
84
115
  };
85
116
  }, [isOpen, updatePosition]);
86
- // Handle click outside
87
117
  useEffect(() => {
88
118
  if (!isOpen) {
89
119
  return;
@@ -0,0 +1,17 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import React from "react";
3
+ import type { ControlSize } from "../../styles/types";
4
+ export interface RatingProps {
5
+ value?: number;
6
+ defaultValue?: number;
7
+ onValueChange?: (value: number) => void;
8
+ count?: number;
9
+ icon?: LucideIcon;
10
+ allowHalf?: boolean;
11
+ size?: ControlSize;
12
+ readOnly?: boolean;
13
+ disabled?: boolean;
14
+ className?: string;
15
+ "aria-label"?: string;
16
+ }
17
+ export declare const Rating: React.ForwardRefExoticComponent<RatingProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,126 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { Star } from "lucide-react";
5
+ import React, { useCallback, useRef, useState } from "react";
6
+ import { Tooltip } from "../Tooltip/index.js";
7
+ import styles from "./Rating.module.css";
8
+ const iconSizeMap = {
9
+ sm: 16,
10
+ md: 20,
11
+ lg: 32,
12
+ };
13
+ export const Rating = React.forwardRef(function Rating({ value: controlledValue, defaultValue = 0, onValueChange, count = 5, icon: IconComponent = Star, allowHalf = false, size = "md", readOnly = false, disabled = false, className, "aria-label": ariaLabel, }, ref) {
14
+ const [internalValue, setInternalValue] = useState(defaultValue);
15
+ const [hoverValue, setHoverValue] = useState(null);
16
+ const containerRef = useRef(null);
17
+ const mergedRef = React.useCallback((node) => {
18
+ containerRef.current = node;
19
+ if (typeof ref === "function") {
20
+ ref(node);
21
+ }
22
+ else if (ref) {
23
+ ref.current = node;
24
+ }
25
+ }, [ref]);
26
+ const isControlled = controlledValue !== undefined;
27
+ const currentValue = isControlled ? controlledValue : internalValue;
28
+ const displayValue = hoverValue !== null && !readOnly && !disabled ? hoverValue : currentValue;
29
+ const iconSize = iconSizeMap[size];
30
+ const setValue = useCallback((next) => {
31
+ if (disabled) {
32
+ return;
33
+ }
34
+ if (!isControlled) {
35
+ setInternalValue(next);
36
+ }
37
+ onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(next);
38
+ }, [disabled, isControlled, onValueChange]);
39
+ const handleKeyDown = useCallback((e) => {
40
+ if (disabled || readOnly) {
41
+ return;
42
+ }
43
+ const step = allowHalf ? 0.5 : 1;
44
+ let newValue = currentValue;
45
+ switch (e.key) {
46
+ case "ArrowRight":
47
+ case "ArrowUp":
48
+ e.preventDefault();
49
+ newValue = Math.min(currentValue + step, count);
50
+ break;
51
+ case "ArrowLeft":
52
+ case "ArrowDown":
53
+ e.preventDefault();
54
+ newValue = Math.max(currentValue - step, 0);
55
+ break;
56
+ case "Home":
57
+ e.preventDefault();
58
+ newValue = 0;
59
+ break;
60
+ case "End":
61
+ e.preventDefault();
62
+ newValue = count;
63
+ break;
64
+ default:
65
+ return;
66
+ }
67
+ setValue(newValue);
68
+ if (containerRef.current) {
69
+ const selector = `[data-value="${newValue}"]`;
70
+ const target = containerRef.current.querySelector(selector);
71
+ target === null || target === void 0 ? void 0 : target.focus();
72
+ }
73
+ }, [disabled, readOnly, allowHalf, currentValue, count, setValue]);
74
+ if (readOnly) {
75
+ return (_jsx("div", { ref: ref, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : `${currentValue} out of ${count}`, className: clsx(styles.rating, styles[size], className), role: "img", children: Array.from({ length: count }, (_, i) => {
76
+ const position = i + 1;
77
+ const isFilled = currentValue >= position;
78
+ const isHalf = !isFilled && currentValue >= position - 0.5;
79
+ return (_jsx("span", { className: styles.iconWrapper, children: isHalf ? (_jsxs(_Fragment, { children: [_jsx("span", { className: clsx(styles.icon, styles.unfilled), children: React.createElement(IconComponent, {
80
+ size: iconSize,
81
+ strokeWidth: 2.5,
82
+ fill: "none",
83
+ }) }), _jsx("span", { className: clsx(styles.icon, styles.filled, styles.halfClip), children: React.createElement(IconComponent, {
84
+ size: iconSize,
85
+ strokeWidth: 2.5,
86
+ fill: "currentColor",
87
+ }) })] })) : (_jsx("span", { className: clsx(styles.icon, isFilled ? styles.filled : styles.unfilled), children: React.createElement(IconComponent, {
88
+ size: iconSize,
89
+ strokeWidth: 2.5,
90
+ fill: isFilled ? "currentColor" : "none",
91
+ }) })) }, position));
92
+ }) }));
93
+ }
94
+ return (_jsx("div", { ref: mergedRef, "aria-label": ariaLabel, className: clsx(styles.rating, styles[size], disabled && styles.disabled, className), role: "radiogroup", onMouseLeave: () => setHoverValue(null), children: allowHalf
95
+ ? Array.from({ length: count }, (_, i) => {
96
+ const position = i + 1;
97
+ const halfValue = position - 0.5;
98
+ const fullValue = position;
99
+ return (_jsxs("span", { className: styles.iconWrapper, children: [_jsx("span", { "aria-hidden": "true", className: clsx(styles.icon, styles.unfilled), children: React.createElement(IconComponent, {
100
+ size: iconSize,
101
+ strokeWidth: 2.5,
102
+ fill: "none",
103
+ }) }), displayValue >= halfValue && (_jsx("span", { "aria-hidden": "true", className: clsx(styles.icon, styles.filled, styles.filledOverlay, displayValue >= fullValue ? undefined : styles.halfClip), children: React.createElement(IconComponent, {
104
+ size: iconSize,
105
+ strokeWidth: 2.5,
106
+ fill: "currentColor",
107
+ }) })), _jsx(Tooltip, { content: `${halfValue} out of ${count}`, placement: "top", children: _jsx("button", { "aria-checked": currentValue >= halfValue, "aria-label": `Rate ${halfValue} out of ${count}`, className: clsx(styles.radioButton, styles.halfButton, styles.halfLeft), "data-value": halfValue, disabled: disabled, role: "radio", tabIndex: currentValue === halfValue ||
108
+ (currentValue === 0 && halfValue === 0.5)
109
+ ? 0
110
+ : -1, type: "button", onClick: () => setValue(halfValue), onKeyDown: handleKeyDown, onMouseEnter: () => setHoverValue(halfValue) }) }), _jsx(Tooltip, { content: `${fullValue} out of ${count}`, placement: "top", children: _jsx("button", { "aria-checked": currentValue >= fullValue, "aria-label": `Rate ${fullValue} out of ${count}`, className: clsx(styles.radioButton, styles.halfButton, styles.halfRight), "data-value": fullValue, disabled: disabled, role: "radio", tabIndex: currentValue === fullValue ? 0 : -1, type: "button", onClick: () => setValue(fullValue), onKeyDown: handleKeyDown, onMouseEnter: () => setHoverValue(fullValue) }) })] }, position));
111
+ })
112
+ : Array.from({ length: count }, (_, i) => {
113
+ const position = i + 1;
114
+ const isFilled = displayValue >= position;
115
+ const label = `${position} out of ${count}`;
116
+ return (_jsx(Tooltip, { content: label, placement: "top", children: _jsx("button", { "aria-checked": currentValue >= position, "aria-label": `Rate ${position} out of ${count}`, className: clsx(styles.iconButton, isFilled && styles.filled, !isFilled && styles.unfilled), "data-value": position, disabled: disabled, role: "radio", tabIndex: currentValue === position ||
117
+ (currentValue === 0 && position === 1)
118
+ ? 0
119
+ : -1, type: "button", onClick: () => setValue(position), onKeyDown: handleKeyDown, onMouseEnter: () => setHoverValue(position), children: React.createElement(IconComponent, {
120
+ size: iconSize,
121
+ strokeWidth: 2.5,
122
+ fill: isFilled ? "currentColor" : "none",
123
+ }) }) }, position));
124
+ }) }));
125
+ });
126
+ Rating.displayName = "Rating";
@@ -0,0 +1,131 @@
1
+ @layer doom.components {
2
+ .rating {
3
+ display: inline-flex;
4
+ align-items: center;
5
+ gap: var(--control-gap);
6
+ position: relative;
7
+ }
8
+ .rating.disabled {
9
+ opacity: 0.6;
10
+ cursor: not-allowed !important;
11
+ background-image: repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(0, 0, 0, 0.05) 10px, rgba(0, 0, 0, 0.05) 20px) !important;
12
+ }
13
+ .rating.disabled:hover {
14
+ transform: none !important;
15
+ filter: none !important;
16
+ }
17
+ .rating.disabled {
18
+ cursor: not-allowed;
19
+ }
20
+ .sm {
21
+ gap: var(--space-1);
22
+ }
23
+ .lg {
24
+ gap: var(--space-2);
25
+ }
26
+ .iconWrapper {
27
+ position: relative;
28
+ display: inline-flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ }
32
+ .icon {
33
+ display: inline-flex;
34
+ transition: transform var(--duration-fast) var(--ease-in-out), color var(--duration-fast) var(--ease-in-out);
35
+ }
36
+ .filled {
37
+ color: var(--warning);
38
+ }
39
+ .unfilled {
40
+ color: var(--muted-foreground);
41
+ }
42
+ .filledOverlay {
43
+ position: absolute;
44
+ inset: 0;
45
+ display: inline-flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ pointer-events: none;
49
+ }
50
+ .halfClip {
51
+ clip-path: inset(0 50% 0 0);
52
+ }
53
+ .iconButton {
54
+ display: inline-flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ padding: 0;
58
+ border: none;
59
+ background: none;
60
+ cursor: pointer;
61
+ transition: transform var(--duration-fast) var(--ease-in-out), color var(--duration-fast) var(--ease-in-out);
62
+ }
63
+ .iconButton:focus-visible:focus-visible {
64
+ outline: none;
65
+ transform: translate(calc(-1 * var(--space-0\.5)), calc(-1 * var(--space-0\.5)));
66
+ box-shadow: var(--shadow-offset-xl) var(--shadow-primary);
67
+ border-color: var(--primary);
68
+ }
69
+ .iconButton:focus-visible:focus {
70
+ outline: none;
71
+ }
72
+ .iconButton:focus-visible {
73
+ border-radius: var(--radius-md);
74
+ }
75
+ .iconButton:focus {
76
+ outline: none;
77
+ }
78
+ .iconButton:hover {
79
+ transform: scale(1.15);
80
+ }
81
+ .iconButton:active {
82
+ transform: scale(0.85);
83
+ }
84
+ .iconButton:disabled {
85
+ cursor: not-allowed;
86
+ pointer-events: none;
87
+ }
88
+ .radioButton {
89
+ position: absolute;
90
+ top: 0;
91
+ bottom: 0;
92
+ padding: 0;
93
+ border: none;
94
+ background: none;
95
+ cursor: pointer;
96
+ z-index: 1;
97
+ }
98
+ .radioButton:focus-visible:focus-visible {
99
+ outline: none;
100
+ transform: translate(calc(-1 * var(--space-0\.5)), calc(-1 * var(--space-0\.5)));
101
+ box-shadow: var(--shadow-offset-xl) var(--shadow-primary);
102
+ border-color: var(--primary);
103
+ }
104
+ .radioButton:focus-visible:focus {
105
+ outline: none;
106
+ }
107
+ .radioButton:focus-visible {
108
+ border-radius: var(--radius-md);
109
+ }
110
+ .radioButton:focus {
111
+ outline: none;
112
+ }
113
+ .radioButton:disabled {
114
+ cursor: not-allowed;
115
+ pointer-events: none;
116
+ }
117
+ .halfLeft {
118
+ left: 0;
119
+ width: 50%;
120
+ }
121
+ .halfRight {
122
+ right: 0;
123
+ width: 50%;
124
+ }
125
+ .iconWrapper:hover .icon {
126
+ transform: scale(1.15);
127
+ }
128
+ .iconWrapper:active .icon {
129
+ transform: scale(0.85);
130
+ }
131
+ }
@@ -0,0 +1 @@
1
+ export * from "./Rating";
@@ -0,0 +1 @@
1
+ export * from "./Rating.js";
@@ -1,7 +1,6 @@
1
1
  import { ColumnDef, Row } from "@tanstack/react-table";
2
2
  import React from "react";
3
- import { type FilterOperatorKey } from "./FilterBuilder/FilterBuilder";
4
- import type { FilterGroupItem } from "./FilterBuilder/FilterGroup";
3
+ import { type FilterDraftGroup, type FilterOperatorKey } from "../../lib/filter";
5
4
  export interface TableProps<T> {
6
5
  data: T[];
7
6
  /**
@@ -16,7 +15,7 @@ export interface TableProps<T> {
16
15
  enableSorting?: boolean;
17
16
  enableVirtualization?: boolean;
18
17
  enableAdvancedFiltering?: boolean;
19
- onAdvancedFilterChange?: (value: FilterGroupItem) => void;
18
+ onAdvancedFilterChange?: (value: FilterDraftGroup) => void;
20
19
  pageSize?: number;
21
20
  height?: string | number;
22
21
  maxHeight?: string | number;