@carbonbits/sixr 1.0.4 → 1.2.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 +162 -5
- package/dist/cart.cjs +41 -0
- package/dist/cart.cjs.map +1 -0
- package/dist/cart.d.cts +21 -0
- package/dist/cart.d.ts +21 -0
- package/dist/cart.js +3 -0
- package/dist/cart.js.map +1 -0
- package/dist/chunk-BHSOSXTU.js +39 -0
- package/dist/chunk-BHSOSXTU.js.map +1 -0
- package/dist/index.cjs +41 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +64 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/text-input.cjs +216 -0
- package/dist/text-input.cjs.map +1 -0
- package/dist/text-input.d.cts +65 -0
- package/dist/text-input.d.ts +65 -0
- package/dist/text-input.js +196 -0
- package/dist/text-input.js.map +1 -0
- package/dist/validation.cjs +43 -0
- package/dist/validation.cjs.map +1 -0
- package/dist/validation.d.cts +28 -0
- package/dist/validation.d.ts +28 -0
- package/dist/validation.js +35 -0
- package/dist/validation.js.map +1 -0
- package/package.json +115 -64
package/README.md
CHANGED
|
@@ -1,7 +1,164 @@
|
|
|
1
|
-
|
|
1
|
+
# Sixr
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
E-commerce geared React component library for [carbonbits](https://carbonbits.co.ke/sixr).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Ships ESM + CJS builds with type declarations, and a prebuilt stylesheet. Static
|
|
6
|
+
components are React Server Component safe and need no `"use client"` boundary in the
|
|
7
|
+
Next.js App Router; interactive ones carry their own directive, so either kind imports
|
|
8
|
+
cleanly into a server component.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
pnpm add @carbonbits/sixr
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`react` and `react-dom` are peer dependencies (`^19.0.0`). `zod` (`^4`) is an optional
|
|
17
|
+
peer dependency, needed only for `@carbonbits/sixr/validation`.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Load the stylesheet once, at the root of your app:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// app/layout.tsx
|
|
25
|
+
import "@carbonbits/sixr/styles.css";
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Each component has its own subpath. Import from there:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
// app/cart/page.tsx
|
|
32
|
+
import { Cart, type CartProduct } from "@carbonbits/sixr/cart";
|
|
33
|
+
|
|
34
|
+
const products: CartProduct[] = [
|
|
35
|
+
{ name: "Stovetop kettle", price: 1500 },
|
|
36
|
+
{ name: "Enamel mug", price: 500 },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export default function Page() {
|
|
40
|
+
return <Cart products={products} currency="KES" locale="en-KE" />;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The root barrel (`@carbonbits/sixr`) re-exports everything and works too, but the
|
|
45
|
+
subpath is the one to reach for.
|
|
46
|
+
|
|
47
|
+
## Components
|
|
48
|
+
|
|
49
|
+
### `Cart`
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { Cart } from "@carbonbits/sixr/cart";
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| Prop | Type | Default | Description |
|
|
56
|
+
| ---------- | --------------- | --------- | --------------------------------------- |
|
|
57
|
+
| `products` | `CartProduct[]` | `[]` | Lines to render. Empty shows a placeholder. |
|
|
58
|
+
| `currency` | `string` | `"KES"` | ISO 4217 code used to format prices. |
|
|
59
|
+
| `locale` | `string` | `"en-KE"` | BCP 47 locale used to format prices. |
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
type CartProduct = {
|
|
63
|
+
/** Stable identity for the line, used as the React key. Falls back to `name`. */
|
|
64
|
+
id?: string;
|
|
65
|
+
name: string;
|
|
66
|
+
price: number;
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Pass an `id` whenever a cart can hold two lines with the same name. Prices are
|
|
71
|
+
formatted with `Intl.NumberFormat`, and the total is summed from `price`.
|
|
72
|
+
|
|
73
|
+
### `TextInput`
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { TextInput } from "@carbonbits/sixr/text-input";
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A labelled text field with optional schema validation. Interactive, so it ships its
|
|
80
|
+
own `"use client"` directive and is reachable only at this subpath — it is not on the
|
|
81
|
+
root barrel, which stays server-safe.
|
|
82
|
+
|
|
83
|
+
| Prop | Type | Default | Description |
|
|
84
|
+
| -------------------- | ------------------------------------ | -------- | ----------------------------------------------------- |
|
|
85
|
+
| `label` | `string` | — | Field label, associated with the input. |
|
|
86
|
+
| `hint` | `ReactNode` | — | Guidance below the field. Replaced by an error. |
|
|
87
|
+
| `error` | `string` | — | Caller-supplied error. Outranks the schema. |
|
|
88
|
+
| `schema` | `TextInputSchema` | — | Any zod schema. Only `safeParse` is called. |
|
|
89
|
+
| `validateOn` | `"blur" \| "change" \| "never"` | `"blur"` | When the schema runs. |
|
|
90
|
+
| `onValidationChange` | `(state: TextInputValidation) => void` | — | Fires on each validation pass. |
|
|
91
|
+
| `onChange` | `(value: string) => void` | — | Receives the value, not the event. |
|
|
92
|
+
| `value` | `string` | — | Makes the field controlled. |
|
|
93
|
+
| `defaultValue` | `string` | `""` | Initial value when uncontrolled. |
|
|
94
|
+
| `optional` | `boolean` | `false` | Shows an "Optional" marker on the label. |
|
|
95
|
+
| `action` | `ReactNode` | — | Trailing label slot, e.g. a "Forgot?" link. |
|
|
96
|
+
| `trailing` | `ReactNode` | — | Trailing in-field slot, e.g. a password peek. |
|
|
97
|
+
| `mono` | `boolean` | `false` | Renders the value in the monospace face. |
|
|
98
|
+
| `type` | `"text" \| "email" \| "password" \| "tel" \| "url" \| "search"` | `"text"` | Input type. |
|
|
99
|
+
|
|
100
|
+
Any other `<input>` attribute — `placeholder`, `maxLength`, `disabled`, `inputMode` —
|
|
101
|
+
is passed straight through.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { TextInput } from "@carbonbits/sixr/text-input";
|
|
105
|
+
import { emailSchema } from "@carbonbits/sixr/validation";
|
|
106
|
+
|
|
107
|
+
<TextInput
|
|
108
|
+
label="Work email"
|
|
109
|
+
type="email"
|
|
110
|
+
placeholder="you@company.com"
|
|
111
|
+
schema={emailSchema}
|
|
112
|
+
onChange={setEmail}
|
|
113
|
+
/>;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Validation runs on blur by default, then on every keystroke once the field has gone
|
|
117
|
+
invalid, so a corrected value clears the message immediately. The first schema issue
|
|
118
|
+
is shown, the input gets `aria-invalid`, and the message is announced via `role="alert"`
|
|
119
|
+
and wired up with `aria-describedby`.
|
|
120
|
+
|
|
121
|
+
`schema` is structurally typed, so any zod schema satisfies it without zod appearing
|
|
122
|
+
in this package's types — you only need zod installed if you use it.
|
|
123
|
+
|
|
124
|
+
## Validation schemas
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
import { emailSchema, passwordSchema } from "@carbonbits/sixr/validation";
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Ready-made zod schemas matching the Carbonbits auth rules: `emailSchema`,
|
|
131
|
+
`passwordSchema`, `verificationCodeSchema`, `backupCodeSchema`, `fullNameSchema`,
|
|
132
|
+
`jobTitleSchema` and `phoneSchema`. They are ordinary zod schemas — extend them with
|
|
133
|
+
`.refine`, or replace the message with `.min(8, "…")`.
|
|
134
|
+
|
|
135
|
+
This subpath needs `zod` (`^4`), which is an optional peer dependency. Nothing else in
|
|
136
|
+
the package requires it.
|
|
137
|
+
|
|
138
|
+
## Styling
|
|
139
|
+
|
|
140
|
+
`dist/styles.css` is compiled by Tailwind 4 and contains only the utilities the
|
|
141
|
+
library itself uses, so it is safe to load alongside a consumer's own Tailwind build.
|
|
142
|
+
|
|
143
|
+
Colours and control metrics come from CSS custom properties, all namespaced `--sixr-*`,
|
|
144
|
+
so nothing here can clobber your own Tailwind theme. Override any of them to retheme
|
|
145
|
+
the library without rebuilding it:
|
|
146
|
+
|
|
147
|
+
```css
|
|
148
|
+
:root {
|
|
149
|
+
--sixr-accent: #0f766e;
|
|
150
|
+
--sixr-line: #d4d4d8;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Dark values follow `prefers-color-scheme` by default. To drive the theme yourself, set
|
|
155
|
+
`data-theme="dark"` or `data-theme="light"` on the root element.
|
|
156
|
+
|
|
157
|
+
## Versioning
|
|
158
|
+
|
|
159
|
+
Released under [semantic versioning](https://semver.org/): breaking changes only land
|
|
160
|
+
in major versions, so `^` ranges are safe to pin against.
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT
|
package/dist/cart.cjs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
|
|
5
|
+
// src/components/cart/index.tsx
|
|
6
|
+
function formatPrice(value, currency, locale) {
|
|
7
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
8
|
+
value
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
function Cart({
|
|
12
|
+
products = [],
|
|
13
|
+
currency = "KES",
|
|
14
|
+
locale = "en-KE"
|
|
15
|
+
}) {
|
|
16
|
+
const total = products.reduce((sum, product) => sum + product.price, 0);
|
|
17
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4", children: [
|
|
18
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-gray-900", children: "Cart" }),
|
|
19
|
+
products.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Your cart is empty." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
20
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col divide-y divide-gray-100", children: products.map((product) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
21
|
+
"li",
|
|
22
|
+
{
|
|
23
|
+
className: "flex items-baseline justify-between gap-4 py-2",
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800", children: product.name }),
|
|
26
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm tabular-nums text-gray-600", children: formatPrice(product.price, currency, locale) })
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
product.id ?? product.name
|
|
30
|
+
)) }),
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2", children: [
|
|
32
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: "Total" }),
|
|
33
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tabular-nums text-gray-900", children: formatPrice(total, currency, locale) })
|
|
34
|
+
] })
|
|
35
|
+
] })
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.Cart = Cart;
|
|
40
|
+
//# sourceMappingURL=cart.cjs.map
|
|
41
|
+
//# sourceMappingURL=cart.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/cart/index.tsx"],"names":["jsxs","jsx","Fragment"],"mappings":";;;;;AAkBA,SAAS,WAAA,CAAY,KAAA,EAAe,QAAA,EAAkB,MAAA,EAAgB;AACpE,EAAA,OAAO,IAAI,KAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,KAAA,EAAO,UAAA,EAAY,QAAA,EAAU,CAAA,CAAE,MAAA;AAAA,IACpE;AAAA,GACF;AACF;AAEO,SAAS,IAAA,CAAK;AAAA,EACnB,WAAW,EAAC;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,MAAA,GAAS;AACX,CAAA,EAAc;AACZ,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,CAAC,KAAK,OAAA,KAAY,GAAA,GAAM,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAEtE,EAAA,uBACEA,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yFAAA,EACb,QAAA,EAAA;AAAA,oBAAAC,cAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,qCAAA,EAAsC,QAAA,EAAA,MAAA,EAAI,CAAA;AAAA,IAEvD,QAAA,CAAS,WAAW,CAAA,mBACnBA,cAAA,CAAC,OAAE,SAAA,EAAU,uBAAA,EAAwB,QAAA,EAAA,qBAAA,EAAmB,CAAA,mBAExDD,eAAA,CAAAE,mBAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAAD,cAAA,CAAC,QAAG,SAAA,EAAU,wCAAA,EACX,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,qBACbD,eAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAU,gDAAA;AAAA,UAEV,QAAA,EAAA;AAAA,4BAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,OAAA,CAAQ,IAAA,EAAK,CAAA;AAAA,4BACtDA,cAAA,CAAC,UAAK,SAAA,EAAU,oCAAA,EACb,sBAAY,OAAA,CAAQ,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EAC9C;AAAA;AAAA,SAAA;AAAA,QANK,OAAA,CAAQ,MAAM,OAAA,CAAQ;AAAA,OAQ9B,CAAA,EACH,CAAA;AAAA,sBAEAD,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yEAAA,EACb,QAAA,EAAA;AAAA,wBAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAoC,QAAA,EAAA,OAAA,EAAK,CAAA;AAAA,wBACzDA,cAAA,CAAC,UAAK,SAAA,EAAU,kDAAA,EACb,sBAAY,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EACtC;AAAA,OAAA,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ","file":"cart.cjs","sourcesContent":["export type CartProduct = {\n /**\n * Stable identity for this line, used as the React key. Falls back to `name`,\n * so pass an `id` whenever a cart can hold two lines with the same name.\n */\n id?: string;\n name: string;\n price: number;\n};\n\nexport type CartProps = {\n products?: CartProduct[];\n /** ISO 4217 code used to format prices. */\n currency?: string;\n /** BCP 47 locale used to format prices. */\n locale?: string;\n};\n\nfunction formatPrice(value: number, currency: string, locale: string) {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(\n value,\n );\n}\n\nexport function Cart({\n products = [],\n currency = \"KES\",\n locale = \"en-KE\",\n}: CartProps) {\n const total = products.reduce((sum, product) => sum + product.price, 0);\n\n return (\n <div className=\"cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4\">\n <h1 className=\"text-xl font-semibold text-gray-900\">Cart</h1>\n\n {products.length === 0 ? (\n <p className=\"text-sm text-gray-500\">Your cart is empty.</p>\n ) : (\n <>\n <ul className=\"flex flex-col divide-y divide-gray-100\">\n {products.map((product) => (\n <li\n key={product.id ?? product.name}\n className=\"flex items-baseline justify-between gap-4 py-2\"\n >\n <span className=\"text-sm text-gray-800\">{product.name}</span>\n <span className=\"text-sm tabular-nums text-gray-600\">\n {formatPrice(product.price, currency, locale)}\n </span>\n </li>\n ))}\n </ul>\n\n <div className=\"flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2\">\n <span className=\"text-sm font-medium text-gray-900\">Total</span>\n <span className=\"text-sm font-semibold tabular-nums text-gray-900\">\n {formatPrice(total, currency, locale)}\n </span>\n </div>\n </>\n )}\n </div>\n );\n}\n"]}
|
package/dist/cart.d.cts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
type CartProduct = {
|
|
4
|
+
/**
|
|
5
|
+
* Stable identity for this line, used as the React key. Falls back to `name`,
|
|
6
|
+
* so pass an `id` whenever a cart can hold two lines with the same name.
|
|
7
|
+
*/
|
|
8
|
+
id?: string;
|
|
9
|
+
name: string;
|
|
10
|
+
price: number;
|
|
11
|
+
};
|
|
12
|
+
type CartProps = {
|
|
13
|
+
products?: CartProduct[];
|
|
14
|
+
/** ISO 4217 code used to format prices. */
|
|
15
|
+
currency?: string;
|
|
16
|
+
/** BCP 47 locale used to format prices. */
|
|
17
|
+
locale?: string;
|
|
18
|
+
};
|
|
19
|
+
declare function Cart({ products, currency, locale, }: CartProps): react.JSX.Element;
|
|
20
|
+
|
|
21
|
+
export { Cart, type CartProduct, type CartProps };
|
package/dist/cart.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
type CartProduct = {
|
|
4
|
+
/**
|
|
5
|
+
* Stable identity for this line, used as the React key. Falls back to `name`,
|
|
6
|
+
* so pass an `id` whenever a cart can hold two lines with the same name.
|
|
7
|
+
*/
|
|
8
|
+
id?: string;
|
|
9
|
+
name: string;
|
|
10
|
+
price: number;
|
|
11
|
+
};
|
|
12
|
+
type CartProps = {
|
|
13
|
+
products?: CartProduct[];
|
|
14
|
+
/** ISO 4217 code used to format prices. */
|
|
15
|
+
currency?: string;
|
|
16
|
+
/** BCP 47 locale used to format prices. */
|
|
17
|
+
locale?: string;
|
|
18
|
+
};
|
|
19
|
+
declare function Cart({ products, currency, locale, }: CartProps): react.JSX.Element;
|
|
20
|
+
|
|
21
|
+
export { Cart, type CartProduct, type CartProps };
|
package/dist/cart.js
ADDED
package/dist/cart.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"cart.js"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
// src/components/cart/index.tsx
|
|
4
|
+
function formatPrice(value, currency, locale) {
|
|
5
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
6
|
+
value
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
function Cart({
|
|
10
|
+
products = [],
|
|
11
|
+
currency = "KES",
|
|
12
|
+
locale = "en-KE"
|
|
13
|
+
}) {
|
|
14
|
+
const total = products.reduce((sum, product) => sum + product.price, 0);
|
|
15
|
+
return /* @__PURE__ */ jsxs("div", { className: "cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4", children: [
|
|
16
|
+
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-gray-900", children: "Cart" }),
|
|
17
|
+
products.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Your cart is empty." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
18
|
+
/* @__PURE__ */ jsx("ul", { className: "flex flex-col divide-y divide-gray-100", children: products.map((product) => /* @__PURE__ */ jsxs(
|
|
19
|
+
"li",
|
|
20
|
+
{
|
|
21
|
+
className: "flex items-baseline justify-between gap-4 py-2",
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-800", children: product.name }),
|
|
24
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm tabular-nums text-gray-600", children: formatPrice(product.price, currency, locale) })
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
product.id ?? product.name
|
|
28
|
+
)) }),
|
|
29
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2", children: [
|
|
30
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-900", children: "Total" }),
|
|
31
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums text-gray-900", children: formatPrice(total, currency, locale) })
|
|
32
|
+
] })
|
|
33
|
+
] })
|
|
34
|
+
] });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { Cart };
|
|
38
|
+
//# sourceMappingURL=chunk-BHSOSXTU.js.map
|
|
39
|
+
//# sourceMappingURL=chunk-BHSOSXTU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/cart/index.tsx"],"names":[],"mappings":";;;AAkBA,SAAS,WAAA,CAAY,KAAA,EAAe,QAAA,EAAkB,MAAA,EAAgB;AACpE,EAAA,OAAO,IAAI,KAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,KAAA,EAAO,UAAA,EAAY,QAAA,EAAU,CAAA,CAAE,MAAA;AAAA,IACpE;AAAA,GACF;AACF;AAEO,SAAS,IAAA,CAAK;AAAA,EACnB,WAAW,EAAC;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,MAAA,GAAS;AACX,CAAA,EAAc;AACZ,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,CAAC,KAAK,OAAA,KAAY,GAAA,GAAM,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAEtE,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yFAAA,EACb,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,qCAAA,EAAsC,QAAA,EAAA,MAAA,EAAI,CAAA;AAAA,IAEvD,QAAA,CAAS,WAAW,CAAA,mBACnB,GAAA,CAAC,OAAE,SAAA,EAAU,uBAAA,EAAwB,QAAA,EAAA,qBAAA,EAAmB,CAAA,mBAExD,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,QAAG,SAAA,EAAU,wCAAA,EACX,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,qBACb,IAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAU,gDAAA;AAAA,UAEV,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,OAAA,CAAQ,IAAA,EAAK,CAAA;AAAA,4BACtD,GAAA,CAAC,UAAK,SAAA,EAAU,oCAAA,EACb,sBAAY,OAAA,CAAQ,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EAC9C;AAAA;AAAA,SAAA;AAAA,QANK,OAAA,CAAQ,MAAM,OAAA,CAAQ;AAAA,OAQ9B,CAAA,EACH,CAAA;AAAA,sBAEA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yEAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAoC,QAAA,EAAA,OAAA,EAAK,CAAA;AAAA,wBACzD,GAAA,CAAC,UAAK,SAAA,EAAU,kDAAA,EACb,sBAAY,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EACtC;AAAA,OAAA,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ","file":"chunk-BHSOSXTU.js","sourcesContent":["export type CartProduct = {\n /**\n * Stable identity for this line, used as the React key. Falls back to `name`,\n * so pass an `id` whenever a cart can hold two lines with the same name.\n */\n id?: string;\n name: string;\n price: number;\n};\n\nexport type CartProps = {\n products?: CartProduct[];\n /** ISO 4217 code used to format prices. */\n currency?: string;\n /** BCP 47 locale used to format prices. */\n locale?: string;\n};\n\nfunction formatPrice(value: number, currency: string, locale: string) {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(\n value,\n );\n}\n\nexport function Cart({\n products = [],\n currency = \"KES\",\n locale = \"en-KE\",\n}: CartProps) {\n const total = products.reduce((sum, product) => sum + product.price, 0);\n\n return (\n <div className=\"cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4\">\n <h1 className=\"text-xl font-semibold text-gray-900\">Cart</h1>\n\n {products.length === 0 ? (\n <p className=\"text-sm text-gray-500\">Your cart is empty.</p>\n ) : (\n <>\n <ul className=\"flex flex-col divide-y divide-gray-100\">\n {products.map((product) => (\n <li\n key={product.id ?? product.name}\n className=\"flex items-baseline justify-between gap-4 py-2\"\n >\n <span className=\"text-sm text-gray-800\">{product.name}</span>\n <span className=\"text-sm tabular-nums text-gray-600\">\n {formatPrice(product.price, currency, locale)}\n </span>\n </li>\n ))}\n </ul>\n\n <div className=\"flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2\">\n <span className=\"text-sm font-medium text-gray-900\">Total</span>\n <span className=\"text-sm font-semibold tabular-nums text-gray-900\">\n {formatPrice(total, currency, locale)}\n </span>\n </div>\n </>\n )}\n </div>\n );\n}\n"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
|
|
5
|
+
// src/components/cart/index.tsx
|
|
6
|
+
function formatPrice(value, currency, locale) {
|
|
7
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
8
|
+
value
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
function Cart({
|
|
12
|
+
products = [],
|
|
13
|
+
currency = "KES",
|
|
14
|
+
locale = "en-KE"
|
|
15
|
+
}) {
|
|
16
|
+
const total = products.reduce((sum, product) => sum + product.price, 0);
|
|
17
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4", children: [
|
|
18
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-gray-900", children: "Cart" }),
|
|
19
|
+
products.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Your cart is empty." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
20
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col divide-y divide-gray-100", children: products.map((product) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
21
|
+
"li",
|
|
22
|
+
{
|
|
23
|
+
className: "flex items-baseline justify-between gap-4 py-2",
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800", children: product.name }),
|
|
26
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm tabular-nums text-gray-600", children: formatPrice(product.price, currency, locale) })
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
product.id ?? product.name
|
|
30
|
+
)) }),
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2", children: [
|
|
32
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: "Total" }),
|
|
33
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tabular-nums text-gray-900", children: formatPrice(total, currency, locale) })
|
|
34
|
+
] })
|
|
35
|
+
] })
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.Cart = Cart;
|
|
40
|
+
//# sourceMappingURL=index.cjs.map
|
|
41
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/cart/index.tsx"],"names":["jsxs","jsx","Fragment"],"mappings":";;;;;AAkBA,SAAS,WAAA,CAAY,KAAA,EAAe,QAAA,EAAkB,MAAA,EAAgB;AACpE,EAAA,OAAO,IAAI,KAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,KAAA,EAAO,UAAA,EAAY,QAAA,EAAU,CAAA,CAAE,MAAA;AAAA,IACpE;AAAA,GACF;AACF;AAEO,SAAS,IAAA,CAAK;AAAA,EACnB,WAAW,EAAC;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,MAAA,GAAS;AACX,CAAA,EAAc;AACZ,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,CAAC,KAAK,OAAA,KAAY,GAAA,GAAM,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAEtE,EAAA,uBACEA,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yFAAA,EACb,QAAA,EAAA;AAAA,oBAAAC,cAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,qCAAA,EAAsC,QAAA,EAAA,MAAA,EAAI,CAAA;AAAA,IAEvD,QAAA,CAAS,WAAW,CAAA,mBACnBA,cAAA,CAAC,OAAE,SAAA,EAAU,uBAAA,EAAwB,QAAA,EAAA,qBAAA,EAAmB,CAAA,mBAExDD,eAAA,CAAAE,mBAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAAD,cAAA,CAAC,QAAG,SAAA,EAAU,wCAAA,EACX,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,qBACbD,eAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAU,gDAAA;AAAA,UAEV,QAAA,EAAA;AAAA,4BAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,OAAA,CAAQ,IAAA,EAAK,CAAA;AAAA,4BACtDA,cAAA,CAAC,UAAK,SAAA,EAAU,oCAAA,EACb,sBAAY,OAAA,CAAQ,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EAC9C;AAAA;AAAA,SAAA;AAAA,QANK,OAAA,CAAQ,MAAM,OAAA,CAAQ;AAAA,OAQ9B,CAAA,EACH,CAAA;AAAA,sBAEAD,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yEAAA,EACb,QAAA,EAAA;AAAA,wBAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAoC,QAAA,EAAA,OAAA,EAAK,CAAA;AAAA,wBACzDA,cAAA,CAAC,UAAK,SAAA,EAAU,kDAAA,EACb,sBAAY,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EACtC;AAAA,OAAA,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ","file":"index.cjs","sourcesContent":["export type CartProduct = {\n /**\n * Stable identity for this line, used as the React key. Falls back to `name`,\n * so pass an `id` whenever a cart can hold two lines with the same name.\n */\n id?: string;\n name: string;\n price: number;\n};\n\nexport type CartProps = {\n products?: CartProduct[];\n /** ISO 4217 code used to format prices. */\n currency?: string;\n /** BCP 47 locale used to format prices. */\n locale?: string;\n};\n\nfunction formatPrice(value: number, currency: string, locale: string) {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(\n value,\n );\n}\n\nexport function Cart({\n products = [],\n currency = \"KES\",\n locale = \"en-KE\",\n}: CartProps) {\n const total = products.reduce((sum, product) => sum + product.price, 0);\n\n return (\n <div className=\"cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4\">\n <h1 className=\"text-xl font-semibold text-gray-900\">Cart</h1>\n\n {products.length === 0 ? (\n <p className=\"text-sm text-gray-500\">Your cart is empty.</p>\n ) : (\n <>\n <ul className=\"flex flex-col divide-y divide-gray-100\">\n {products.map((product) => (\n <li\n key={product.id ?? product.name}\n className=\"flex items-baseline justify-between gap-4 py-2\"\n >\n <span className=\"text-sm text-gray-800\">{product.name}</span>\n <span className=\"text-sm tabular-nums text-gray-600\">\n {formatPrice(product.price, currency, locale)}\n </span>\n </li>\n ))}\n </ul>\n\n <div className=\"flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2\">\n <span className=\"text-sm font-medium text-gray-900\">Total</span>\n <span className=\"text-sm font-semibold tabular-nums text-gray-900\">\n {formatPrice(total, currency, locale)}\n </span>\n </div>\n </>\n )}\n </div>\n );\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export { Cart, CartProduct, CartProps } from './cart.cjs';
|
|
2
|
+
import { InputHTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Structural shape of a zod schema — every zod schema satisfies it, and any
|
|
6
|
+
* version of zod does. Declaring it this way rather than importing `ZodType`
|
|
7
|
+
* keeps zod out of this module's public types, so consumers who only want an
|
|
8
|
+
* input do not have to install zod to typecheck it.
|
|
9
|
+
*/
|
|
10
|
+
type TextInputSchema = {
|
|
11
|
+
safeParse: (value: unknown) => {
|
|
12
|
+
success: true;
|
|
13
|
+
} | {
|
|
14
|
+
success: false;
|
|
15
|
+
error: {
|
|
16
|
+
issues: readonly {
|
|
17
|
+
message: string;
|
|
18
|
+
}[];
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/** Outcome of a single validation pass, handed to `onValidationChange`. */
|
|
23
|
+
type TextInputValidation = {
|
|
24
|
+
valid: boolean;
|
|
25
|
+
/** First issue reported by the schema, or `undefined` when valid. */
|
|
26
|
+
error?: string;
|
|
27
|
+
};
|
|
28
|
+
type TextInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "defaultValue" | "type"> & {
|
|
29
|
+
label?: string;
|
|
30
|
+
/** Guidance under the field. Replaced by the error message while invalid. */
|
|
31
|
+
hint?: ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Error supplied by the caller — a server-side or cross-field failure. Takes
|
|
34
|
+
* precedence over the schema, so a form can hold a field invalid regardless
|
|
35
|
+
* of what `schema` makes of the value on its own.
|
|
36
|
+
*/
|
|
37
|
+
error?: string;
|
|
38
|
+
/** Renders the design's muted "Optional" marker at the end of the label. */
|
|
39
|
+
optional?: boolean;
|
|
40
|
+
/** Trailing label slot, for affordances like the design's "Forgot?" link. */
|
|
41
|
+
action?: ReactNode;
|
|
42
|
+
/** Trailing in-field slot, for affordances like the design's password peek. */
|
|
43
|
+
trailing?: ReactNode;
|
|
44
|
+
/** Renders the value in the design's monospace face. */
|
|
45
|
+
mono?: boolean;
|
|
46
|
+
type?: "text" | "email" | "password" | "tel" | "url" | "search";
|
|
47
|
+
value?: string;
|
|
48
|
+
defaultValue?: string;
|
|
49
|
+
onChange?: (value: string) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Any zod schema that accepts a string. Only `safeParse` is used, so a
|
|
52
|
+
* refined, transformed or preprocessed schema works as well as a plain one.
|
|
53
|
+
*/
|
|
54
|
+
schema?: TextInputSchema;
|
|
55
|
+
/**
|
|
56
|
+
* When the schema runs. `blur` — the default — avoids scolding someone
|
|
57
|
+
* mid-keystroke; once a field has gone invalid it revalidates on every
|
|
58
|
+
* keystroke so the message clears as soon as it is fixed.
|
|
59
|
+
*/
|
|
60
|
+
validateOn?: "blur" | "change" | "never";
|
|
61
|
+
onValidationChange?: (state: TextInputValidation) => void;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type { TextInputProps, TextInputSchema, TextInputValidation };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export { Cart, CartProduct, CartProps } from './cart.js';
|
|
2
|
+
import { InputHTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Structural shape of a zod schema — every zod schema satisfies it, and any
|
|
6
|
+
* version of zod does. Declaring it this way rather than importing `ZodType`
|
|
7
|
+
* keeps zod out of this module's public types, so consumers who only want an
|
|
8
|
+
* input do not have to install zod to typecheck it.
|
|
9
|
+
*/
|
|
10
|
+
type TextInputSchema = {
|
|
11
|
+
safeParse: (value: unknown) => {
|
|
12
|
+
success: true;
|
|
13
|
+
} | {
|
|
14
|
+
success: false;
|
|
15
|
+
error: {
|
|
16
|
+
issues: readonly {
|
|
17
|
+
message: string;
|
|
18
|
+
}[];
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/** Outcome of a single validation pass, handed to `onValidationChange`. */
|
|
23
|
+
type TextInputValidation = {
|
|
24
|
+
valid: boolean;
|
|
25
|
+
/** First issue reported by the schema, or `undefined` when valid. */
|
|
26
|
+
error?: string;
|
|
27
|
+
};
|
|
28
|
+
type TextInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "defaultValue" | "type"> & {
|
|
29
|
+
label?: string;
|
|
30
|
+
/** Guidance under the field. Replaced by the error message while invalid. */
|
|
31
|
+
hint?: ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Error supplied by the caller — a server-side or cross-field failure. Takes
|
|
34
|
+
* precedence over the schema, so a form can hold a field invalid regardless
|
|
35
|
+
* of what `schema` makes of the value on its own.
|
|
36
|
+
*/
|
|
37
|
+
error?: string;
|
|
38
|
+
/** Renders the design's muted "Optional" marker at the end of the label. */
|
|
39
|
+
optional?: boolean;
|
|
40
|
+
/** Trailing label slot, for affordances like the design's "Forgot?" link. */
|
|
41
|
+
action?: ReactNode;
|
|
42
|
+
/** Trailing in-field slot, for affordances like the design's password peek. */
|
|
43
|
+
trailing?: ReactNode;
|
|
44
|
+
/** Renders the value in the design's monospace face. */
|
|
45
|
+
mono?: boolean;
|
|
46
|
+
type?: "text" | "email" | "password" | "tel" | "url" | "search";
|
|
47
|
+
value?: string;
|
|
48
|
+
defaultValue?: string;
|
|
49
|
+
onChange?: (value: string) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Any zod schema that accepts a string. Only `safeParse` is used, so a
|
|
52
|
+
* refined, transformed or preprocessed schema works as well as a plain one.
|
|
53
|
+
*/
|
|
54
|
+
schema?: TextInputSchema;
|
|
55
|
+
/**
|
|
56
|
+
* When the schema runs. `blur` — the default — avoids scolding someone
|
|
57
|
+
* mid-keystroke; once a field has gone invalid it revalidates on every
|
|
58
|
+
* keystroke so the message clears as soon as it is fixed.
|
|
59
|
+
*/
|
|
60
|
+
validateOn?: "blur" | "change" | "never";
|
|
61
|
+
onValidationChange?: (state: TextInputValidation) => void;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type { TextInputProps, TextInputSchema, TextInputValidation };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-white:#fff;--spacing:.25rem;--container-md:28rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.top-1\/2{top:50%}.right-\[9px\]{right:9px}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.contents{display:contents}.flex{display:flex}.inline{display:inline}.table{display:table}.h-10{height:calc(var(--spacing) * 10)}.w-full{width:100%}.max-w-md{max-width:var(--container-md)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.flex-col{flex-direction:column}.items-baseline{align-items:baseline}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-4{gap:calc(var(--spacing) * 4)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}.rounded-field{border-radius:4px}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-danger{border-color:var(--sixr-danger)}.border-gray-200{border-color:var(--color-gray-200)}.border-line{border-color:var(--sixr-line)}.bg-surface{background-color:var(--sixr-surface)}.bg-white{background-color:var(--color-white)}.p-4{padding:calc(var(--spacing) * 4)}.px-3{padding-inline:calc(var(--spacing) * 3)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pr-16{padding-right:calc(var(--spacing) * 16)}.font-\[inherit\]{font-family:inherit}.font-code{font-family:var(--sixr-font-code)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-field{font-size:13px}.text-hint{font-size:11px}.text-label{font-size:11.5px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-danger{color:var(--sixr-danger)}.text-fg{color:var(--sixr-fg)}.text-fg-muted{color:var(--sixr-fg-muted)}.text-fg-subtle{color:var(--sixr-fg-subtle)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:text-fg-subtle::placeholder{color:var(--sixr-fg-subtle)}.focus\:border-accent:focus{border-color:var(--sixr-accent)}.focus\:border-danger:focus{border-color:var(--sixr-danger)}.focus\:ring-3:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-accent-soft:focus{--tw-ring-color:var(--sixr-accent-soft)}.focus\:ring-danger-soft:focus{--tw-ring-color:var(--sixr-danger-soft)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-45:disabled{opacity:.45}}:root{--sixr-bg:#f4f4f5;--sixr-surface:#fff;--sixr-surface-2:#f4f4f6;--sixr-surface-3:#eaeaec;--sixr-line:#e2e2e5;--sixr-line-strong:#cdcdd2;--sixr-fg:#0b0b0d;--sixr-fg-muted:#55555e;--sixr-fg-subtle:#8e8e99;--sixr-accent:#0b0b0d;--sixr-accent-2:#000;--sixr-accent-soft:#ededef;--sixr-on-accent:#fff;--sixr-success:#16a34a;--sixr-success-soft:#e9f5ec;--sixr-warning:#b45309;--sixr-info:#2563eb;--sixr-danger:#dc2626;--sixr-danger-soft:#fdecec;--sixr-font-code:"JetBrains Mono", ui-monospace, monospace}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--sixr-bg:#000;--sixr-surface:#0b0b0d;--sixr-surface-2:#121215;--sixr-surface-3:#1b1b1f;--sixr-line:#232328;--sixr-line-strong:#3a3a42;--sixr-fg:#fff;--sixr-fg-muted:#a3a3ad;--sixr-fg-subtle:#70707b;--sixr-accent:#fff;--sixr-accent-2:#e4e4e8;--sixr-accent-soft:#17171b;--sixr-on-accent:#000;--sixr-success:#34d399;--sixr-success-soft:#12301e;--sixr-warning:#fbbf24;--sixr-info:#60a5fa;--sixr-danger:#f87171;--sixr-danger-soft:#2a1416}}:root[data-theme=dark]{--sixr-bg:#000;--sixr-surface:#0b0b0d;--sixr-surface-2:#121215;--sixr-surface-3:#1b1b1f;--sixr-line:#232328;--sixr-line-strong:#3a3a42;--sixr-fg:#fff;--sixr-fg-muted:#a3a3ad;--sixr-fg-subtle:#70707b;--sixr-accent:#fff;--sixr-accent-2:#e4e4e8;--sixr-accent-soft:#17171b;--sixr-on-accent:#000;--sixr-success:#34d399;--sixr-success-soft:#12301e;--sixr-warning:#fbbf24;--sixr-info:#60a5fa;--sixr-danger:#f87171;--sixr-danger-soft:#2a1416}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
|