@voyantjs/extras-ui 0.13.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.
- package/README.md +13 -0
- package/dist/components/product-combobox.d.ts +9 -0
- package/dist/components/product-combobox.d.ts.map +1 -0
- package/dist/components/product-combobox.js +42 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @voyantjs/extras-ui
|
|
2
|
+
|
|
3
|
+
Importable React UI components for Voyant extras. Bundler-consumed (Vite, Next.js, webpack, etc.).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @voyantjs/extras-ui @voyantjs/extras-react @voyantjs/voyant-ui @tanstack/react-query react react-dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`@voyantjs/voyant-ui` provides the design-system primitives. `@voyantjs/extras-react` provides the data-layer hooks. Both are required peers.
|
|
12
|
+
|
|
13
|
+
All components accept a `className` prop and merge it with `cn()`. Wrap or compose to extend; use the registry copy-paste path (`npx shadcn add @voyant/...`) for components you want to fork outright.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
value: string | null | undefined;
|
|
3
|
+
onChange: (value: string | null) => void;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function ProductCombobox({ value, onChange, placeholder, disabled, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=product-combobox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-combobox.d.ts","sourceRoot":"","sources":["../../src/components/product-combobox.tsx"],"names":[],"mappings":"AAcA,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAID,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,WAAgC,EAChC,QAAQ,GACT,EAAE,KAAK,2CAkEP"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useProduct, useProducts } from "@voyantjs/products-react";
|
|
4
|
+
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyantjs/voyant-ui/components/combobox";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
const PAGE_SIZE = 25;
|
|
7
|
+
export function ProductCombobox({ value, onChange, placeholder = "Search products…", disabled, }) {
|
|
8
|
+
const [search, setSearch] = React.useState("");
|
|
9
|
+
const listQuery = useProducts({ search: search || undefined, limit: PAGE_SIZE });
|
|
10
|
+
const selectedQuery = useProduct(value ?? undefined, { enabled: !!value });
|
|
11
|
+
const items = React.useMemo(() => {
|
|
12
|
+
const map = new Map();
|
|
13
|
+
for (const item of listQuery.data?.data ?? [])
|
|
14
|
+
map.set(item.id, item);
|
|
15
|
+
if (selectedQuery.data)
|
|
16
|
+
map.set(selectedQuery.data.id, selectedQuery.data);
|
|
17
|
+
return Array.from(map.values());
|
|
18
|
+
}, [listQuery.data?.data, selectedQuery.data]);
|
|
19
|
+
const itemMap = React.useMemo(() => new Map(items.map((item) => [item.id, item])), [items]);
|
|
20
|
+
const selected = value ? itemMap.get(value) : undefined;
|
|
21
|
+
const selectedLabel = selected ? selected.name : "";
|
|
22
|
+
const [inputValue, setInputValue] = React.useState(selectedLabel);
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
if (selectedLabel)
|
|
25
|
+
setInputValue(selectedLabel);
|
|
26
|
+
}, [selectedLabel]);
|
|
27
|
+
return (_jsxs(Combobox, { items: items.map((item) => item.id), value: value ?? null, inputValue: inputValue, autoHighlight: true, disabled: disabled, itemToStringValue: (id) => itemMap.get(id)?.name ?? "", onInputValueChange: (next) => {
|
|
28
|
+
setInputValue(next);
|
|
29
|
+
setSearch(next);
|
|
30
|
+
if (!next)
|
|
31
|
+
onChange(null);
|
|
32
|
+
}, onValueChange: (next) => {
|
|
33
|
+
const id = next ?? null;
|
|
34
|
+
onChange(id);
|
|
35
|
+
setInputValue(id ? (itemMap.get(id)?.name ?? "") : "");
|
|
36
|
+
}, children: [_jsx(ComboboxInput, { placeholder: placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending ? "Loading…" : "No products found." }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
37
|
+
const item = itemMap.get(id);
|
|
38
|
+
if (!item)
|
|
39
|
+
return null;
|
|
40
|
+
return (_jsx(ComboboxItem, { value: item.id, children: _jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate font-medium", children: item.name }), _jsxs("span", { className: "truncate text-xs text-muted-foreground", children: [item.status, " \u00B7 ", item.bookingMode] })] }) }, item.id));
|
|
41
|
+
} }) })] })] }));
|
|
42
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ProductCombobox } from "./components/product-combobox";
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voyantjs/extras-ui",
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/voyantjs/voyant.git",
|
|
8
|
+
"directory": "packages/extras-ui"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./src/index.ts",
|
|
14
|
+
"./components/*": "./src/components/*.tsx"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.build.json",
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"prepack": "pnpm run build",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"lint": "biome check src/",
|
|
22
|
+
"test": "vitest run --passWithNoTests"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@tanstack/react-query": "^5.0.0",
|
|
26
|
+
"@tanstack/react-table": "^8.0.0",
|
|
27
|
+
"@voyantjs/extras-react": "workspace:*",
|
|
28
|
+
"@voyantjs/products-react": "workspace:*",
|
|
29
|
+
"@voyantjs/voyant-ui": "workspace:*",
|
|
30
|
+
"react": "^19.0.0",
|
|
31
|
+
"react-dom": "^19.0.0",
|
|
32
|
+
"react-hook-form": "^7.60.0",
|
|
33
|
+
"zod": "^3.25.76"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@tanstack/react-query": "^5.96.2",
|
|
37
|
+
"@tanstack/react-table": "^8.21.3",
|
|
38
|
+
"@types/react": "^19.2.14",
|
|
39
|
+
"@types/react-dom": "^19.2.3",
|
|
40
|
+
"@voyantjs/extras-react": "workspace:*",
|
|
41
|
+
"@voyantjs/products-react": "workspace:*",
|
|
42
|
+
"@voyantjs/voyant-typescript-config": "workspace:*",
|
|
43
|
+
"@voyantjs/voyant-ui": "workspace:*",
|
|
44
|
+
"lucide-react": "^0.475.0",
|
|
45
|
+
"react": "^19.2.4",
|
|
46
|
+
"react-dom": "^19.2.4",
|
|
47
|
+
"react-hook-form": "^7.60.0",
|
|
48
|
+
"typescript": "^6.0.2",
|
|
49
|
+
"vitest": "^4.1.2",
|
|
50
|
+
"zod": "^3.25.76"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist"
|
|
54
|
+
],
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public",
|
|
57
|
+
"exports": {
|
|
58
|
+
".": {
|
|
59
|
+
"types": "./dist/index.d.ts",
|
|
60
|
+
"import": "./dist/index.js"
|
|
61
|
+
},
|
|
62
|
+
"./components/*": {
|
|
63
|
+
"types": "./dist/components/*.d.ts",
|
|
64
|
+
"import": "./dist/components/*.js"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"main": "./dist/index.js",
|
|
68
|
+
"types": "./dist/index.d.ts"
|
|
69
|
+
}
|
|
70
|
+
}
|