@purpurds/quantity-selector 0.0.1

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.
@@ -0,0 +1 @@
1
+ ._purpur-quantity-selector_lzxe3_1 [class*=_purpur-text-field__field-row]{position:relative;height:44px}._purpur-quantity-selector_lzxe3_1 [class*=_purpur-text-field__input-container]{width:56px}._purpur-quantity-selector_lzxe3_1 [class*=_purpur-text-field__input-container] input{text-align:center}._purpur-quantity-selector_lzxe3_1 [class*=_purpur-text-field__input-container] input::-webkit-outer-spin-button,._purpur-quantity-selector_lzxe3_1 [class*=_purpur-text-field__input-container] input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}._purpur-quantity-selector_lzxe3_1 [class*=_purpur-text-field__input-container] input[type=number]{-moz-appearance:textfield}._purpur-quantity-selector--full-width_lzxe3_21,._purpur-quantity-selector--full-width_lzxe3_21 [class*=_purpur-text-field__input-container]{width:100%}._purpur-quantity-selector--loading_lzxe3_27 [class*=_purpur-text-field__input-container]>input{display:none}._purpur-quantity-selector--no-gap_lzxe3_30 [class*=_purpur-text-field__frame]{border-radius:0;border-left:none;border-right:none}._purpur-quantity-selector--no-gap-disabled-left_lzxe3_35 [class*=_purpur-text-field__frame]{border-radius:0;border-right:none}._purpur-quantity-selector--no-gap-disabled-right_lzxe3_39 [class*=_purpur-text-field__frame]{border-radius:0;border-left:none}._purpur-quantity-selector--no-gap-disabled_lzxe3_35 [class*=_purpur-text-field__frame]{border-radius:0}._purpur-quantity-selector__spinner_lzxe3_46{position:absolute;z-index:1;top:50%;left:60px;transform:translateY(-50%)}._purpur-quantity-selector__spinner--full-width_lzxe3_53{left:50%;transform:translate(-50%,-50%)}._purpur-quantity-selector__spinner--separated_lzxe3_57{left:68px}._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button_qjlbs_1{padding:var(--purpur-spacing-100) calc(var(--purpur-spacing-150) + var(--purpur-spacing-25))}._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button--gap-left_qjlbs_4{margin-right:var(--purpur-spacing-100)}._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button--gap-right_qjlbs_7{margin-left:var(--purpur-spacing-100)}._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button--no-gap-right_qjlbs_10{border-radius:0 var(--purpur-border-radius-md) var(--purpur-border-radius-md) 0}._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button_qjlbs_1._purpur-quantity-selector-button--no-gap-left_qjlbs_13{border-radius:var(--purpur-border-radius-md) 0 0 var(--purpur-border-radius-md)}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@purpurds/quantity-selector",
3
+ "version": "0.0.1",
4
+ "license": "AGPL-3.0-only",
5
+ "main": "./dist/quantity-selector.cjs.js",
6
+ "types": "./dist/quantity-selector.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/quantity-selector.cjs.js",
10
+ "types": "./dist/quantity-selector.d.ts",
11
+ "default": "./dist/quantity-selector.es.js"
12
+ },
13
+ "./styles": "./dist/styles.css"
14
+ },
15
+ "source": "src/quantity-selector.tsx",
16
+ "dependencies": {
17
+ "classnames": "~2.5.0",
18
+ "@purpurds/button": "5.23.0",
19
+ "@purpurds/spinner": "5.23.0",
20
+ "@purpurds/icon": "5.23.0",
21
+ "@purpurds/text-field": "5.23.0"
22
+ },
23
+ "devDependencies": {
24
+ "@rushstack/eslint-patch": "~1.10.0",
25
+ "@storybook/blocks": "^8.3.5",
26
+ "@storybook/preview-api": "^8.3.5",
27
+ "@storybook/react": "^8.3.5",
28
+ "@telia/base-rig": "~8.2.0",
29
+ "@telia/react-rig": "~3.2.0",
30
+ "@testing-library/dom": "~9.3.3",
31
+ "@testing-library/jest-dom": "~6.4.0",
32
+ "@testing-library/react": "~14.3.0",
33
+ "@types/node": "20.12.12",
34
+ "@types/react-dom": "^18.3.0",
35
+ "@types/react": "^18.3.3",
36
+ "eslint-plugin-testing-library": "~6.2.0",
37
+ "eslint": "^8.57.0",
38
+ "jsdom": "~22.1.0",
39
+ "lint-staged": "~10.5.3",
40
+ "prettier": "~2.8.8",
41
+ "react-dom": "^18.3.1",
42
+ "react": "^18.3.1",
43
+ "storybook": "^8.3.5",
44
+ "typescript": "^5.6.3",
45
+ "vite": "5.4.8",
46
+ "vitest": "^2.1.2",
47
+ "@purpurds/component-rig": "1.0.0"
48
+ },
49
+ "scripts": {
50
+ "build:dev": "vite",
51
+ "build:watch": "vite build --watch",
52
+ "build": "vite build",
53
+ "ci:build": "rushx build",
54
+ "coverage": "vitest run --coverage",
55
+ "lint:fix": "eslint . --fix",
56
+ "lint": "lint-staged --no-stash 2>&1",
57
+ "sbdev": "rush sbdev",
58
+ "test:unit": "vitest run --passWithNoTests",
59
+ "test:watch": "vitest --watch",
60
+ "test": "rushx test:unit",
61
+ "typecheck": "tsc -p ./tsconfig.json"
62
+ }
63
+ }
@@ -0,0 +1,4 @@
1
+ declare module "*.scss" {
2
+ const styles: { [className: string]: string };
3
+ export default styles;
4
+ }
@@ -0,0 +1,23 @@
1
+ .purpur-quantity-selector-button {
2
+ $root: &;
3
+
4
+ &#{$root}#{$root} {
5
+ padding: var(--purpur-spacing-100) calc(var(--purpur-spacing-150) + var(--purpur-spacing-25));
6
+
7
+ &--gap-left {
8
+ margin-right: var(--purpur-spacing-100);
9
+ }
10
+
11
+ &--gap-right {
12
+ margin-left: var(--purpur-spacing-100);
13
+ }
14
+
15
+ &--no-gap-right {
16
+ border-radius: 0 var(--purpur-border-radius-md) var(--purpur-border-radius-md) 0;
17
+ }
18
+
19
+ &--no-gap-left {
20
+ border-radius: var(--purpur-border-radius-md) 0 0 var(--purpur-border-radius-md);
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,56 @@
1
+ import React from "react";
2
+ import { Button } from "@purpurds/button";
3
+ import { IconMinus, IconPlus } from "@purpurds/icon";
4
+ import c from "classnames/bind";
5
+
6
+ import styles from "./quantity-selector-button.module.scss";
7
+
8
+ type variant = "primary" | "secondary";
9
+ type buttonType = "increment" | "decrement";
10
+
11
+ type QuantitySelectorButtonProps = {
12
+ ["data-testid"]?: string;
13
+ disabled?: boolean;
14
+ hasGap: boolean;
15
+ variant: variant;
16
+ buttonType: buttonType;
17
+ onClick: (buttonType: buttonType) => void;
18
+ };
19
+
20
+ const cx = c.bind(styles);
21
+ const rootClassName = "purpur-quantity-selector-button";
22
+
23
+ export const QuantitySelectorButton = ({
24
+ ["data-testid"]: dataTestid,
25
+ disabled,
26
+ hasGap,
27
+ variant,
28
+ buttonType,
29
+ onClick,
30
+ }: QuantitySelectorButtonProps) => {
31
+ const classes = cx([
32
+ rootClassName,
33
+ {
34
+ [`${rootClassName}--gap-left`]: hasGap && buttonType === "decrement",
35
+ [`${rootClassName}--gap-right`]: hasGap && buttonType === "increment",
36
+ [`${rootClassName}--no-gap-left`]: !hasGap && buttonType === "decrement",
37
+ [`${rootClassName}--no-gap-right`]: !hasGap && buttonType === "increment",
38
+ },
39
+ ]);
40
+
41
+ return (
42
+ <Button
43
+ data-testid={dataTestid}
44
+ className={classes}
45
+ size="sm"
46
+ type="submit"
47
+ variant={variant}
48
+ onClick={() => onClick(buttonType)}
49
+ iconOnly={true}
50
+ disabled={disabled}
51
+ tabIndex={-1}
52
+ >
53
+ {buttonType === "increment" ? <IconPlus size="xs" /> : <IconMinus size="xs" />}
54
+ </Button>
55
+ );
56
+ };
@@ -0,0 +1,83 @@
1
+ .purpur-quantity-selector {
2
+ $root: &;
3
+
4
+ [class*="_purpur-text-field__field-row"] {
5
+ position: relative;
6
+ height: 44px;
7
+ }
8
+
9
+ [class*="_purpur-text-field__input-container"] {
10
+ width: 56px;
11
+ input {
12
+ text-align: center;
13
+ }
14
+
15
+ /* Chrome, Safari, Edge, Opera */
16
+ input::-webkit-outer-spin-button,
17
+ input::-webkit-inner-spin-button {
18
+ -webkit-appearance: none;
19
+ margin: 0;
20
+ }
21
+
22
+ /* Firefox */
23
+ input[type=number] {
24
+ -moz-appearance: textfield;
25
+ }
26
+ }
27
+
28
+ &--full-width {
29
+ width: 100%;
30
+ [class*="_purpur-text-field__input-container"] {
31
+ width: 100%;
32
+ }
33
+ }
34
+
35
+ &--loading {
36
+ [class*="_purpur-text-field__input-container"] > input {
37
+ display: none;
38
+ }
39
+ }
40
+
41
+ &--no-gap {
42
+ [class*="_purpur-text-field__frame"] {
43
+ border-radius: 0;
44
+ border-left: none;
45
+ border-right: none;
46
+ }
47
+ }
48
+
49
+ &--no-gap-disabled-left {
50
+ [class*="_purpur-text-field__frame"] {
51
+ border-radius: 0;
52
+ border-right: none;
53
+ }
54
+ }
55
+
56
+ &--no-gap-disabled-right {
57
+ [class*="_purpur-text-field__frame"] {
58
+ border-radius: 0;
59
+ border-left: none;
60
+ }
61
+ }
62
+
63
+ &--no-gap-disabled {
64
+ [class*="_purpur-text-field__frame"] {
65
+ border-radius: 0;
66
+ }
67
+ }
68
+
69
+ &__spinner {
70
+ position: absolute;
71
+ z-index: 1;
72
+ top: 50%;
73
+ left: 60px;
74
+ transform: translate(0%, -50%);
75
+ &--full-width {
76
+ left: 50%;
77
+ transform: translate(-50%, -50%);
78
+ }
79
+ &--separated {
80
+ left: 68px;
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,72 @@
1
+ import React from "react";
2
+ import { useArgs } from "@storybook/preview-api";
3
+ import type { Meta, StoryObj } from "@storybook/react";
4
+
5
+ import "@purpurds/text-field/styles";
6
+ import "@purpurds/button/styles";
7
+ import "@purpurds/icon/styles";
8
+ import "@purpurds/spinner/styles";
9
+ import {
10
+ QuantitySelector,
11
+ quantitySelectorButtonVariants,
12
+ quantitySelectorVariants,
13
+ } from "./quantity-selector";
14
+
15
+ const meta = {
16
+ title: "Inputs/QuantitySelector",
17
+ component: QuantitySelector,
18
+ args: {
19
+ variant: "attached",
20
+ buttonVariant: "primary",
21
+ errorText: "",
22
+ disabled: false,
23
+ loading: false,
24
+ },
25
+ argTypes: {
26
+ variant: {
27
+ control: "select",
28
+ options: quantitySelectorVariants,
29
+ table: { type: { summary: quantitySelectorVariants.map((x) => `"${x}"`).join(" | ") } },
30
+ },
31
+ buttonVariant: {
32
+ control: "select",
33
+ options: quantitySelectorButtonVariants,
34
+ table: { type: { summary: quantitySelectorButtonVariants.map((x) => `"${x}"`).join(" | ") } },
35
+ },
36
+ },
37
+ parameters: {
38
+ design: [
39
+ {
40
+ name: "QuantitySelector",
41
+ type: "figma",
42
+ url: "https://www.figma.com/design/XEaIIFskrrxIBHMZDkIuIg/Purpur-DS---Component-library-%26-guidelines?node-id=56156-3059",
43
+ },
44
+ ],
45
+ },
46
+ render: ({ ...args }) => {
47
+ const [{ value }, updateArgs] = useArgs(); // eslint-disable-line react-hooks/rules-of-hooks
48
+
49
+ const handleOnChange = (value: number) => {
50
+ updateArgs({ value });
51
+ };
52
+
53
+ return <QuantitySelector {...args} value={value} onChange={handleOnChange} />;
54
+ },
55
+ } satisfies Meta<typeof QuantitySelector>;
56
+
57
+ export default meta;
58
+ type Story = StoryObj<typeof QuantitySelector>;
59
+
60
+ export const Showcase: Story = {
61
+ args: {
62
+ variant: "attached",
63
+ buttonVariant: "primary",
64
+ },
65
+ };
66
+
67
+ export const Seperated: Story = {
68
+ args: {
69
+ variant: "separated",
70
+ buttonVariant: "secondary",
71
+ },
72
+ };
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+ import * as matchers from "@testing-library/jest-dom/matchers";
3
+ import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+
6
+ import { QuantitySelector, QuantitySelectorProps } from "./quantity-selector";
7
+
8
+ expect.extend(matchers);
9
+
10
+ const testid = "quantity-selector";
11
+
12
+ const props: QuantitySelectorProps = {
13
+ ["data-testid"]: testid,
14
+ variant: "attached",
15
+ buttonVariant: "secondary",
16
+ onChange: vi.fn(),
17
+ };
18
+
19
+ describe("QuantitySelector", () => {
20
+ afterEach(cleanup);
21
+
22
+ it("should render component with primary attached buttons", () => {
23
+ render(<QuantitySelector {...props} variant="attached" buttonVariant="primary" />);
24
+ expect(screen.queryByTestId(testid + "-input")).toBeInTheDocument();
25
+ expect(screen.queryByTestId(testid + "-button-increment")).toBeInTheDocument();
26
+ expect(screen.queryByTestId(testid + "-button-decrement")).toBeInTheDocument();
27
+ });
28
+
29
+ it("should render component with secondary separated buttons", () => {
30
+ render(<QuantitySelector {...props} variant="separated" buttonVariant="secondary" />);
31
+ expect(screen.queryByTestId(testid + "-input")).toBeInTheDocument();
32
+ expect(screen.queryByTestId(testid + "-button-increment")).toBeInTheDocument();
33
+ expect(screen.queryByTestId(testid + "-button-decrement")).toBeInTheDocument();
34
+ });
35
+
36
+ it("should increment value when pressing increment button", () => {
37
+ const onChangeMock = vi.fn();
38
+ render(<QuantitySelector {...props} onChange={onChangeMock} />);
39
+
40
+ const button = screen.getByTestId(testid + "-button-increment");
41
+ fireEvent.click(button);
42
+
43
+ expect(onChangeMock).toHaveBeenCalledTimes(1);
44
+ expect(onChangeMock).toHaveBeenCalledWith(1);
45
+ });
46
+
47
+ it("should decrease value when pressing decrement button", () => {
48
+ const onChangeMock = vi.fn();
49
+ render(<QuantitySelector {...props} value={5} onChange={onChangeMock} />);
50
+
51
+ const button = screen.getByTestId(testid + "-button-decrement");
52
+ fireEvent.click(button);
53
+
54
+ expect(onChangeMock).toHaveBeenCalledTimes(1);
55
+ expect(onChangeMock).toHaveBeenCalledWith(4);
56
+ });
57
+
58
+ it("should not decrease value below zero when pressing decrement button", () => {
59
+ const onChangeMock = vi.fn();
60
+ render(<QuantitySelector {...props} value={0} onChange={onChangeMock} />);
61
+
62
+ const button = screen.getByTestId(testid + "-button-decrement");
63
+ fireEvent.click(button);
64
+
65
+ expect(onChangeMock).toHaveBeenCalledTimes(0);
66
+ });
67
+
68
+ it("should update value when entering a number in input", async () => {
69
+ const onChangeMock = vi.fn();
70
+ render(<QuantitySelector {...props} onChange={onChangeMock} />);
71
+
72
+ const input = screen.getByTestId(testid + "-input");
73
+ fireEvent.change(input, { target: { value: "5" } });
74
+
75
+ await waitFor(() => expect(onChangeMock).toHaveBeenCalledTimes(1));
76
+ expect(onChangeMock).toHaveBeenCalledWith(5);
77
+ });
78
+ });
@@ -0,0 +1,174 @@
1
+ import React, { ChangeEvent, ForwardedRef, forwardRef, useEffect, useState } from "react";
2
+ import { Spinner } from "@purpurds/spinner";
3
+ import { TextField } from "@purpurds/text-field";
4
+ import c from "classnames/bind";
5
+
6
+ import styles from "./quantity-selector.module.scss";
7
+ import { QuantitySelectorButton } from "./quantity-selector-button";
8
+
9
+ const cx = c.bind(styles);
10
+
11
+ export const quantitySelectorVariants = ["attached", "separated"] as const;
12
+ export const quantitySelectorButtonVariants = ["primary", "secondary"] as const;
13
+
14
+ type Variant = (typeof quantitySelectorVariants)[number];
15
+ type ButtonVariant = (typeof quantitySelectorButtonVariants)[number];
16
+
17
+ export type QuantitySelectorProps = {
18
+ ["data-testid"]?: string;
19
+ className?: string;
20
+ variant: Variant;
21
+ buttonVariant: ButtonVariant;
22
+ onChange: (value: number) => void;
23
+ value?: number;
24
+ minValue?: number;
25
+ maxValue?: number;
26
+ label?: string;
27
+ errorText?: string;
28
+ fullWidth?: boolean;
29
+ disabled?: boolean;
30
+ loading?: boolean;
31
+ };
32
+
33
+ const rootClassName = "purpur-quantity-selector";
34
+
35
+ export const QuantitySelector = forwardRef(
36
+ (
37
+ {
38
+ ["data-testid"]: dataTestId,
39
+ className,
40
+ variant,
41
+ buttonVariant,
42
+ onChange,
43
+ value: defaultValue,
44
+ fullWidth,
45
+ disabled,
46
+ loading,
47
+ minValue,
48
+ maxValue,
49
+ ...props
50
+ }: QuantitySelectorProps,
51
+ ref: ForwardedRef<HTMLInputElement>
52
+ ) => {
53
+ const [value, setValue] = useState(defaultValue ?? 0);
54
+ const [debouncedValue, setDebouncedValue] = useState(value);
55
+ const getTestId = (name: string) => (dataTestId ? `${dataTestId}-${name}` : undefined);
56
+ const isDisabled = disabled || loading;
57
+ const min = Math.max(minValue ?? 0, 0);
58
+ const max = maxValue ?? Number.MAX_SAFE_INTEGER;
59
+ const incrementDisabled = value === max;
60
+ const decrementDisabled = value === min;
61
+
62
+ useEffect(() => {
63
+ if (debouncedValue === value) {
64
+ return;
65
+ }
66
+ const timer = setTimeout(() => {
67
+ setValue(debouncedValue);
68
+ onChange(debouncedValue);
69
+ }, 300);
70
+
71
+ return () => {
72
+ clearTimeout(timer);
73
+ };
74
+ }, [debouncedValue, value, onChange]);
75
+
76
+ const classes = cx([
77
+ className,
78
+ rootClassName,
79
+ {
80
+ [`${rootClassName}--no-gap`]:
81
+ variant === "attached" && !isDisabled && !decrementDisabled && !incrementDisabled,
82
+ [`${rootClassName}--no-gap-disabled-left`]:
83
+ variant === "attached" && !isDisabled && !incrementDisabled && decrementDisabled,
84
+ [`${rootClassName}--no-gap-disabled-right`]:
85
+ variant === "attached" && !isDisabled && !decrementDisabled && incrementDisabled,
86
+ [`${rootClassName}--no-gap-disabled`]:
87
+ variant === "attached" && (isDisabled || (incrementDisabled && decrementDisabled)),
88
+ [`${rootClassName}--full-width`]: fullWidth,
89
+ [`${rootClassName}--loading`]: loading,
90
+ },
91
+ ]);
92
+
93
+ const handleOnChange = (e: ChangeEvent<HTMLInputElement>) => {
94
+ const numericRegex = /^[0-9]*$/;
95
+ if (!numericRegex.test(e.target.value)) {
96
+ return;
97
+ }
98
+ const val = +e.target.value;
99
+ if (val < min || val > max) {
100
+ return;
101
+ }
102
+ setDebouncedValue(val);
103
+ };
104
+
105
+ const handleOnClick = (adjustmentType: "increment" | "decrement") => {
106
+ const adjustedValue = adjustmentType === "increment" ? value + 1 : value - 1;
107
+ if (adjustedValue < 0) {
108
+ return;
109
+ }
110
+ setValue(adjustedValue);
111
+ setDebouncedValue(adjustedValue);
112
+ onChange(adjustedValue);
113
+ };
114
+
115
+ const adjustmentButton = (adjustmentType: "increment" | "decrement") => {
116
+ const buttonDisabled =
117
+ (adjustmentType === "increment" && incrementDisabled) ||
118
+ (adjustmentType === "decrement" && decrementDisabled);
119
+ return (
120
+ <QuantitySelectorButton
121
+ data-testid={getTestId("button-" + adjustmentType)}
122
+ variant={buttonVariant}
123
+ buttonType={adjustmentType}
124
+ hasGap={variant === "separated"}
125
+ onClick={handleOnClick}
126
+ disabled={isDisabled || buttonDisabled}
127
+ />
128
+ );
129
+ };
130
+
131
+ const beforeField = (
132
+ <>
133
+ {loading && (
134
+ <div
135
+ className={cx([
136
+ `${rootClassName}__spinner`,
137
+ {
138
+ [`${rootClassName}__spinner--full-width`]: fullWidth,
139
+ [`${rootClassName}__spinner--separated`]: !fullWidth && variant === "separated",
140
+ },
141
+ ])}
142
+ >
143
+ <Spinner
144
+ key="spinner"
145
+ disabled={isDisabled}
146
+ size="xs"
147
+ data-testid={getTestId("spinner")}
148
+ />
149
+ </div>
150
+ )}
151
+ {adjustmentButton("decrement")}
152
+ </>
153
+ );
154
+
155
+ return (
156
+ <TextField
157
+ value={debouncedValue}
158
+ type="number"
159
+ ref={ref}
160
+ className={classes}
161
+ data-testid={dataTestId}
162
+ onChange={handleOnChange}
163
+ beforeField={beforeField}
164
+ afterField={adjustmentButton("increment")}
165
+ disabled={isDisabled}
166
+ min={min}
167
+ max={max}
168
+ {...props}
169
+ />
170
+ );
171
+ }
172
+ );
173
+
174
+ QuantitySelector.displayName = "QuantitySelector";
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "@purpurds/component-rig",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ },
6
+ "include": ["src"]
7
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@purpurds/component-rig/tsconfig.types.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["**/*.test.*", "**/*.stories.*"]
9
+ }
@@ -0,0 +1,5 @@
1
+ import { defineConfig } from "vite";
2
+ import { getConfig } from "@purpurds/component-rig/vite.config";
3
+ import pkg from "./package.json";
4
+
5
+ export default defineConfig(getConfig(pkg));