@edux-design/chips 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.
- package/README.md +64 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +125 -0
- package/dist/index.mjs +89 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @edux-design/chips
|
|
2
|
+
|
|
3
|
+
Token-driven filter chips that compose existing primitives (like `TagNumber`) without extra props. Each chip renders a pill with a count, label, and optional dismiss control that mirrors the spec in `chips.png`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @edux-design/chips @edux-design/utils @edux-design/icons
|
|
11
|
+
# or
|
|
12
|
+
npm install @edux-design/chips @edux-design/utils @edux-design/icons
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The package ships React components and depends on `react@^19.1.0` / `react-dom@^19.1.0`.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```jsx
|
|
22
|
+
import { Chip } from "@edux-design/chips";
|
|
23
|
+
import { TagNumber } from "@edux-design/tags";
|
|
24
|
+
|
|
25
|
+
export function Filters({ onRemove }) {
|
|
26
|
+
return (
|
|
27
|
+
<Chip label="Label" onRemove={onRemove}>
|
|
28
|
+
<TagNumber variant="brand" tone="solid">999</TagNumber>
|
|
29
|
+
</Chip>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Key Props
|
|
35
|
+
|
|
36
|
+
| Prop | Description |
|
|
37
|
+
| ---- | ----------- |
|
|
38
|
+
| `label` | Primary text rendered inside the chip (truncated when needed). |
|
|
39
|
+
| `size` | `"default"` (40px tall) or `"small"` (32px tall). |
|
|
40
|
+
| `onRemove` | When provided, renders a close icon button that calls the handler. |
|
|
41
|
+
| `removeLabel` | Accessible label for the close button. Defaults to `"Remove chip"`. |
|
|
42
|
+
| `disabled` | Mutes colors, disables the close button, and removes pointer interactions. |
|
|
43
|
+
|
|
44
|
+
`children` are rendered before the label, so you can compose `TagNumber` (or any other primitive) directly inside the chip without a dedicated prop.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Development
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm --filter @edux-design/chips lint
|
|
52
|
+
pnpm --filter @edux-design/chips check-types
|
|
53
|
+
pnpm --filter @edux-design/chips build
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Stories live under `src/demos/Chip.stories.jsx` with size + state examples pulled from `chips.png`.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Notes
|
|
61
|
+
|
|
62
|
+
- Focus, hover, pressed, dragged, and inactive visuals are all handled via Tailwind utility classes wired to the shared design tokens.
|
|
63
|
+
- The chip container uses `focus-within` so the focus ring appears when the remove button is tabbed to.
|
|
64
|
+
- Feel free to slot other components ahead of the label; the layout simply spaces whatever you supply, so TagNumber composition stays entirely declarative.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Token-driven chip that composes existing primitives (i.e. TagNumber) inside.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {React.HTMLAttributes<HTMLDivElement> & {
|
|
7
|
+
* label?: React.ReactNode,
|
|
8
|
+
* size?: "default"|"small",
|
|
9
|
+
* onRemove?: (event: React.MouseEvent<HTMLButtonElement>) => void,
|
|
10
|
+
* removeLabel?: string,
|
|
11
|
+
* disabled?: boolean,
|
|
12
|
+
* }} ChipProps
|
|
13
|
+
*/
|
|
14
|
+
declare const Chip: react.ForwardRefExoticComponent<react.RefAttributes<any>>;
|
|
15
|
+
|
|
16
|
+
export { Chip };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Token-driven chip that composes existing primitives (i.e. TagNumber) inside.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {React.HTMLAttributes<HTMLDivElement> & {
|
|
7
|
+
* label?: React.ReactNode,
|
|
8
|
+
* size?: "default"|"small",
|
|
9
|
+
* onRemove?: (event: React.MouseEvent<HTMLButtonElement>) => void,
|
|
10
|
+
* removeLabel?: string,
|
|
11
|
+
* disabled?: boolean,
|
|
12
|
+
* }} ChipProps
|
|
13
|
+
*/
|
|
14
|
+
declare const Chip: react.ForwardRefExoticComponent<react.RefAttributes<any>>;
|
|
15
|
+
|
|
16
|
+
export { Chip };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/index.js
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
32
|
+
Chip: () => Chip
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/elements/Chip.jsx
|
|
37
|
+
var import_react = __toESM(require("react"));
|
|
38
|
+
var import_utils = require("@edux-design/utils");
|
|
39
|
+
var import_icons = require("@edux-design/icons");
|
|
40
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
41
|
+
var SIZE_MAP = {
|
|
42
|
+
default: {
|
|
43
|
+
container: "text-sm min-h-[40px] px-8 py-6 gap-8",
|
|
44
|
+
close: "h-[16px] w-[16px]"
|
|
45
|
+
},
|
|
46
|
+
small: {
|
|
47
|
+
container: "text-xs min-h-[32px] px-8 py-4 gap-6",
|
|
48
|
+
close: "h-[16px] w-[16px]"
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var BASE_STYLES = (0, import_utils.cx)(
|
|
52
|
+
"inline-flex items-center rounded-full border border-border-minimal bg-bg-base text-fg-base w-fit",
|
|
53
|
+
"transition-all duration-200 ease-out",
|
|
54
|
+
"hover:border-border-primary-base hover:bg-bg-primary-subtle",
|
|
55
|
+
"active:border-border-primary-emphasis active:bg-bg-primary-subtle-90",
|
|
56
|
+
"focus-within:shadow-focus focus-within:border-border-emphasis",
|
|
57
|
+
"shadow-sm"
|
|
58
|
+
);
|
|
59
|
+
var DISABLED_STYLES = "bg-bg-inactive text-fg-inactive border-border-subtle cursor-not-allowed pointer-events-none";
|
|
60
|
+
var Chip = (0, import_react.forwardRef)(function Chip2({
|
|
61
|
+
label,
|
|
62
|
+
children,
|
|
63
|
+
size = "default",
|
|
64
|
+
onRemove,
|
|
65
|
+
removeLabel = "Remove chip",
|
|
66
|
+
disabled = false,
|
|
67
|
+
className,
|
|
68
|
+
...rest
|
|
69
|
+
}, ref) {
|
|
70
|
+
const sizeStyles = SIZE_MAP[size] ?? SIZE_MAP.default;
|
|
71
|
+
const containerClass = (0, import_utils.cx)(
|
|
72
|
+
BASE_STYLES,
|
|
73
|
+
sizeStyles.container,
|
|
74
|
+
disabled && DISABLED_STYLES,
|
|
75
|
+
className
|
|
76
|
+
);
|
|
77
|
+
const shouldRenderLabel = label !== void 0 && label !== null && label !== false;
|
|
78
|
+
const shouldRenderChildren = children !== void 0 && children !== null && !(Array.isArray(children) && children.length === 0);
|
|
79
|
+
const normalizedChildren = import_react.default.Children.toArray(children).filter(
|
|
80
|
+
(child) => child !== null && child !== void 0 && child !== false
|
|
81
|
+
);
|
|
82
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
83
|
+
"div",
|
|
84
|
+
{
|
|
85
|
+
ref,
|
|
86
|
+
className: containerClass,
|
|
87
|
+
"data-disabled": disabled ? "true" : void 0,
|
|
88
|
+
...rest,
|
|
89
|
+
children: [
|
|
90
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex min-w-0 flex-1 items-center gap-6", children: [
|
|
91
|
+
shouldRenderChildren && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex items-center gap-4 shrink-0 [&>*]:shrink-0", children: normalizedChildren.map(
|
|
92
|
+
(child, index) => (0, import_react.isValidElement)(child) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex items-center", children: child }, child.key ?? index) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: child }, index)
|
|
93
|
+
) }),
|
|
94
|
+
shouldRenderLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate text-left", children: label })
|
|
95
|
+
] }),
|
|
96
|
+
onRemove && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
97
|
+
"button",
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
disabled,
|
|
101
|
+
"aria-label": removeLabel,
|
|
102
|
+
onClick: (event) => {
|
|
103
|
+
event.stopPropagation();
|
|
104
|
+
onRemove(event);
|
|
105
|
+
},
|
|
106
|
+
className: (0, import_utils.cx)(
|
|
107
|
+
"ml-4 inline-flex items-center justify-center rounded-full text-fg-subtle",
|
|
108
|
+
"transition-colors duration-150 ease-in-out",
|
|
109
|
+
"hover:bg-bg-primary-subtle hover:text-fg-primary-base",
|
|
110
|
+
"focus-visible:shadow-focus focus-visible:outline-none",
|
|
111
|
+
"active:bg-bg-primary-subtle-90",
|
|
112
|
+
disabled && "opacity-60 pointer-events-none bg-transparent text-fg-inactive",
|
|
113
|
+
sizeStyles.close
|
|
114
|
+
),
|
|
115
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.Close, {})
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
123
|
+
0 && (module.exports = {
|
|
124
|
+
Chip
|
|
125
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/elements/Chip.jsx
|
|
2
|
+
import React, { forwardRef, isValidElement } from "react";
|
|
3
|
+
import { cx } from "@edux-design/utils";
|
|
4
|
+
import { Close } from "@edux-design/icons";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
var SIZE_MAP = {
|
|
7
|
+
default: {
|
|
8
|
+
container: "text-sm min-h-[40px] px-8 py-6 gap-8",
|
|
9
|
+
close: "h-[16px] w-[16px]"
|
|
10
|
+
},
|
|
11
|
+
small: {
|
|
12
|
+
container: "text-xs min-h-[32px] px-8 py-4 gap-6",
|
|
13
|
+
close: "h-[16px] w-[16px]"
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var BASE_STYLES = cx(
|
|
17
|
+
"inline-flex items-center rounded-full border border-border-minimal bg-bg-base text-fg-base w-fit",
|
|
18
|
+
"transition-all duration-200 ease-out",
|
|
19
|
+
"hover:border-border-primary-base hover:bg-bg-primary-subtle",
|
|
20
|
+
"active:border-border-primary-emphasis active:bg-bg-primary-subtle-90",
|
|
21
|
+
"focus-within:shadow-focus focus-within:border-border-emphasis",
|
|
22
|
+
"shadow-sm"
|
|
23
|
+
);
|
|
24
|
+
var DISABLED_STYLES = "bg-bg-inactive text-fg-inactive border-border-subtle cursor-not-allowed pointer-events-none";
|
|
25
|
+
var Chip = forwardRef(function Chip2({
|
|
26
|
+
label,
|
|
27
|
+
children,
|
|
28
|
+
size = "default",
|
|
29
|
+
onRemove,
|
|
30
|
+
removeLabel = "Remove chip",
|
|
31
|
+
disabled = false,
|
|
32
|
+
className,
|
|
33
|
+
...rest
|
|
34
|
+
}, ref) {
|
|
35
|
+
const sizeStyles = SIZE_MAP[size] ?? SIZE_MAP.default;
|
|
36
|
+
const containerClass = cx(
|
|
37
|
+
BASE_STYLES,
|
|
38
|
+
sizeStyles.container,
|
|
39
|
+
disabled && DISABLED_STYLES,
|
|
40
|
+
className
|
|
41
|
+
);
|
|
42
|
+
const shouldRenderLabel = label !== void 0 && label !== null && label !== false;
|
|
43
|
+
const shouldRenderChildren = children !== void 0 && children !== null && !(Array.isArray(children) && children.length === 0);
|
|
44
|
+
const normalizedChildren = React.Children.toArray(children).filter(
|
|
45
|
+
(child) => child !== null && child !== void 0 && child !== false
|
|
46
|
+
);
|
|
47
|
+
return /* @__PURE__ */ jsxs(
|
|
48
|
+
"div",
|
|
49
|
+
{
|
|
50
|
+
ref,
|
|
51
|
+
className: containerClass,
|
|
52
|
+
"data-disabled": disabled ? "true" : void 0,
|
|
53
|
+
...rest,
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-6", children: [
|
|
56
|
+
shouldRenderChildren && /* @__PURE__ */ jsx("span", { className: "flex items-center gap-4 shrink-0 [&>*]:shrink-0", children: normalizedChildren.map(
|
|
57
|
+
(child, index) => isValidElement(child) ? /* @__PURE__ */ jsx("span", { className: "flex items-center", children: child }, child.key ?? index) : /* @__PURE__ */ jsx("span", { children: child }, index)
|
|
58
|
+
) }),
|
|
59
|
+
shouldRenderLabel && /* @__PURE__ */ jsx("span", { className: "truncate text-left", children: label })
|
|
60
|
+
] }),
|
|
61
|
+
onRemove && /* @__PURE__ */ jsx(
|
|
62
|
+
"button",
|
|
63
|
+
{
|
|
64
|
+
type: "button",
|
|
65
|
+
disabled,
|
|
66
|
+
"aria-label": removeLabel,
|
|
67
|
+
onClick: (event) => {
|
|
68
|
+
event.stopPropagation();
|
|
69
|
+
onRemove(event);
|
|
70
|
+
},
|
|
71
|
+
className: cx(
|
|
72
|
+
"ml-4 inline-flex items-center justify-center rounded-full text-fg-subtle",
|
|
73
|
+
"transition-colors duration-150 ease-in-out",
|
|
74
|
+
"hover:bg-bg-primary-subtle hover:text-fg-primary-base",
|
|
75
|
+
"focus-visible:shadow-focus focus-visible:outline-none",
|
|
76
|
+
"active:bg-bg-primary-subtle-90",
|
|
77
|
+
disabled && "opacity-60 pointer-events-none bg-transparent text-fg-inactive",
|
|
78
|
+
sizeStyles.close
|
|
79
|
+
),
|
|
80
|
+
children: /* @__PURE__ */ jsx(Close, {})
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
export {
|
|
88
|
+
Chip
|
|
89
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@edux-design/chips",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"sideEffects": [
|
|
6
|
+
"**/*.css"
|
|
7
|
+
],
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"lint": "eslint . --max-warnings 0",
|
|
21
|
+
"build": "tsup src/index.js --format esm,cjs --dts",
|
|
22
|
+
"dev": "tsup src/index.js --watch --format esm,cjs --dts",
|
|
23
|
+
"generate:component": "turbo gen react-component",
|
|
24
|
+
"check-types": "tsc --noEmit"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": "^5.9.2",
|
|
28
|
+
"tailwindcss": "^4.1.12",
|
|
29
|
+
"postcss": "^8.5.6",
|
|
30
|
+
"tsup": "^8.5.0",
|
|
31
|
+
"@tailwindcss/cli": "^4.1.12",
|
|
32
|
+
"@tailwindcss/postcss": "^4.1.12"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@edux-design/utils": "*",
|
|
36
|
+
"@edux-design/icons": "*",
|
|
37
|
+
"react": "^19.1.0",
|
|
38
|
+
"react-dom": "^19.1.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"react": "^19.1.0",
|
|
42
|
+
"react-dom": "^19.1.0"
|
|
43
|
+
}
|
|
44
|
+
}
|