ionbase-ui 0.1.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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/Icon.d.ts +72 -0
- package/dist/Icon.d.ts.map +1 -0
- package/dist/Icon.js +50 -0
- package/dist/Icon.js.map +1 -0
- package/dist/components/Avatar.d.ts +52 -0
- package/dist/components/Avatar.d.ts.map +1 -0
- package/dist/components/Avatar.js +59 -0
- package/dist/components/Avatar.js.map +1 -0
- package/dist/components/Badge.d.ts +26 -0
- package/dist/components/Badge.d.ts.map +1 -0
- package/dist/components/Badge.js +17 -0
- package/dist/components/Badge.js.map +1 -0
- package/dist/components/Button.d.ts +18 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +43 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Checkbox.d.ts +30 -0
- package/dist/components/Checkbox.d.ts.map +1 -0
- package/dist/components/Checkbox.js +39 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/Divider.d.ts +17 -0
- package/dist/components/Divider.d.ts.map +1 -0
- package/dist/components/Divider.js +16 -0
- package/dist/components/Divider.js.map +1 -0
- package/dist/components/Header.d.ts +25 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +18 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/Input.d.ts +32 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +66 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/Logo.d.ts +41 -0
- package/dist/components/Logo.d.ts.map +1 -0
- package/dist/components/Logo.js +70 -0
- package/dist/components/Logo.js.map +1 -0
- package/dist/components/Menu.d.ts +29 -0
- package/dist/components/Menu.d.ts.map +1 -0
- package/dist/components/Menu.js +31 -0
- package/dist/components/Menu.js.map +1 -0
- package/dist/components/NavItem.d.ts +30 -0
- package/dist/components/NavItem.d.ts.map +1 -0
- package/dist/components/NavItem.js +45 -0
- package/dist/components/NavItem.js.map +1 -0
- package/dist/components/Radio.d.ts +30 -0
- package/dist/components/Radio.d.ts.map +1 -0
- package/dist/components/Radio.js +61 -0
- package/dist/components/Radio.js.map +1 -0
- package/dist/components/ScrollProgress.d.ts +33 -0
- package/dist/components/ScrollProgress.d.ts.map +1 -0
- package/dist/components/ScrollProgress.js +115 -0
- package/dist/components/ScrollProgress.js.map +1 -0
- package/dist/components/Select.d.ts +43 -0
- package/dist/components/Select.d.ts.map +1 -0
- package/dist/components/Select.js +80 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/Table.d.ts +71 -0
- package/dist/components/Table.d.ts.map +1 -0
- package/dist/components/Table.js +60 -0
- package/dist/components/Table.js.map +1 -0
- package/dist/components/Tabs.d.ts +23 -0
- package/dist/components/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs.js +47 -0
- package/dist/components/Tabs.js.map +1 -0
- package/dist/components/Toggle.d.ts +25 -0
- package/dist/components/Toggle.d.ts.map +1 -0
- package/dist/components/Toggle.js +32 -0
- package/dist/components/Toggle.js.map +1 -0
- package/dist/components/index.d.ts +33 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +17 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/styles/avatar.css +138 -0
- package/dist/styles/badge.css +125 -0
- package/dist/styles/button.css +331 -0
- package/dist/styles/checkbox.css +229 -0
- package/dist/styles/divider.css +31 -0
- package/dist/styles/header.css +95 -0
- package/dist/styles/index.css +44 -0
- package/dist/styles/input.css +264 -0
- package/dist/styles/logo.css +63 -0
- package/dist/styles/menu.css +150 -0
- package/dist/styles/nav-item.css +120 -0
- package/dist/styles/radio.css +200 -0
- package/dist/styles/scroll-progress.css +171 -0
- package/dist/styles/select.css +162 -0
- package/dist/styles/table.css +192 -0
- package/dist/styles/tabs.css +219 -0
- package/dist/styles/toggle.css +184 -0
- package/dist/styles/tokens/base.css +386 -0
- package/dist/styles/tokens/breakpoint-mobile.css +34 -0
- package/dist/styles/tokens/breakpoint-tablet.css +25 -0
- package/dist/styles/tokens/elevation.css +112 -0
- package/dist/styles/tokens/index.css +6 -0
- package/dist/styles/tokens/theme-dark.css +106 -0
- package/dist/styles/tokens/typography.css +124 -0
- package/dist/tokens/index.d.ts +2097 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +1702 -0
- package/dist/tokens/index.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 IonBase
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ionbase-ui
|
|
2
|
+
|
|
3
|
+
The IonBase Design System: accessible React components, their stylesheets, and
|
|
4
|
+
the design tokens they are built on — in one package.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm i ionbase-ui
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
React 18 or 19 is a peer dependency.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
Import the stylesheet once, at your app entry:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import 'ionbase-ui/styles';
|
|
18
|
+
import { Button } from 'ionbase-ui';
|
|
19
|
+
|
|
20
|
+
export function App() {
|
|
21
|
+
return <Button intent="primary">Save</Button>;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That single import pulls in the design tokens, the typography (Host Grotesk,
|
|
26
|
+
Merriweather, Space Mono), and every component stylesheet. Dark mode is
|
|
27
|
+
`data-theme="dark"` on any ancestor — no separate import, no JS.
|
|
28
|
+
|
|
29
|
+
## Icons
|
|
30
|
+
|
|
31
|
+
The system does not ship an icon set. `Icon` takes the icon as a prop, so any
|
|
32
|
+
SVG component works — [lucide-react](https://lucide.dev), heroicons,
|
|
33
|
+
react-icons, or your own:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Icon } from 'ionbase-ui';
|
|
37
|
+
import { Plus } from 'lucide-react';
|
|
38
|
+
|
|
39
|
+
<Icon as={Plus} size="sm" />
|
|
40
|
+
<Icon as={Plus} label="Add item" /> // meaningful icon, gets an a11y name
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Omit `label` for decorative icons and they are hidden from assistive tech
|
|
44
|
+
instead of announced twice. `size` picks a rung on the `icon-size` ladder —
|
|
45
|
+
`xs` 12, `sm` 16, `md` 20, `lg` 24, `xl` 32 — or takes any CSS length. Omit it
|
|
46
|
+
and the icon inherits the surrounding font size, which is what lets an icon sit
|
|
47
|
+
correctly inside a Button.
|
|
48
|
+
|
|
49
|
+
## Entry points
|
|
50
|
+
|
|
51
|
+
| Import | What it is |
|
|
52
|
+
| --------------------- | ---------------------------------------------- |
|
|
53
|
+
| `ionbase-ui` | Components, `Icon`, and the `tokens` object |
|
|
54
|
+
| `ionbase-ui/styles` | Everything: tokens + typography + components |
|
|
55
|
+
| `ionbase-ui/styles/*` | One component stylesheet, e.g. `button.css` |
|
|
56
|
+
| `ionbase-ui/tokens` | Token custom properties only, no component CSS |
|
|
57
|
+
| `ionbase-ui/tokens/*` | One token layer, e.g. `theme-dark.css` |
|
|
58
|
+
|
|
59
|
+
Token values are also available as JavaScript, as `var(--…)` references:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { tokens } from 'ionbase-ui';
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Tokens come from Figma
|
|
66
|
+
|
|
67
|
+
Every token name and value in this package is generated from the IonBase Figma
|
|
68
|
+
file. They are not authored here and should not be edited here — see
|
|
69
|
+
[the repo](https://github.com/raza-ahmed/ionbase-design-system) for the
|
|
70
|
+
pipeline and its gates.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
package/dist/Icon.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Any component that renders an SVG element and accepts SVG props.
|
|
4
|
+
*
|
|
5
|
+
* Typed structurally rather than as lucide-react's `LucideIcon`, so the icon
|
|
6
|
+
* set is the consumer's choice: lucide, heroicons, react-icons, a hand-rolled
|
|
7
|
+
* SVG component. Naming one library in the type would have made it a
|
|
8
|
+
* dependency of every install for the sake of a type that is erased at build
|
|
9
|
+
* time.
|
|
10
|
+
*/
|
|
11
|
+
export type IconComponent = React.ComponentType<React.SVGProps<SVGSVGElement> & React.RefAttributes<SVGSVGElement>>;
|
|
12
|
+
/**
|
|
13
|
+
* The `icon-size` ladder, verbatim.
|
|
14
|
+
*
|
|
15
|
+
* These map one-to-one onto the Semantics tokens `icon-size/xs...xl`, so `md`
|
|
16
|
+
* here and `icon-size/md` in Figma are the same 20px. There is no separate
|
|
17
|
+
* decision being made in this file.
|
|
18
|
+
*
|
|
19
|
+
* BREAKING, AND DELIBERATELY SO
|
|
20
|
+
*
|
|
21
|
+
* This used to be two sizes where `sm` was 16 and `md` was **24**. That made
|
|
22
|
+
* `md` mean `icon-size/lg`, so the one word meant two different things
|
|
23
|
+
* depending on whether you were reading code or Figma — the exact ambiguity the
|
|
24
|
+
* ladder exists to remove. Callers who want the old `md` now ask for `lg`.
|
|
25
|
+
*
|
|
26
|
+
* Nothing inside the system depended on it: Button and Tabs size their icons in
|
|
27
|
+
* CSS from `--icon-size-*`, never through this prop.
|
|
28
|
+
*/
|
|
29
|
+
declare const SIZE_TOKENS: {
|
|
30
|
+
readonly xs: "var(--icon-size-xs)";
|
|
31
|
+
readonly sm: "var(--icon-size-sm)";
|
|
32
|
+
readonly md: "var(--icon-size-md)";
|
|
33
|
+
readonly lg: "var(--icon-size-lg)";
|
|
34
|
+
readonly xl: "var(--icon-size-xl)";
|
|
35
|
+
};
|
|
36
|
+
export type IconSize = keyof typeof SIZE_TOKENS;
|
|
37
|
+
export interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, 'ref'> {
|
|
38
|
+
/**
|
|
39
|
+
* The icon component to render, e.g. `import { Plus } from 'lucide-react'`.
|
|
40
|
+
* Any SVG component works — the design system does not ship an icon set.
|
|
41
|
+
*/
|
|
42
|
+
as: IconComponent;
|
|
43
|
+
/**
|
|
44
|
+
* A rung on the `icon-size` ladder — `xs` 12, `sm` 16, `md` 20, `lg` 24,
|
|
45
|
+
* `xl` 32 — or any CSS length for a one-off. Omit to inherit the surrounding
|
|
46
|
+
* font size, which is what lets an icon sit correctly inside a Button without
|
|
47
|
+
* the Button having to know anything about this component.
|
|
48
|
+
*/
|
|
49
|
+
size?: IconSize | (string & {});
|
|
50
|
+
/**
|
|
51
|
+
* Accessible name. Provide it when the icon carries meaning on its own — an
|
|
52
|
+
* icon-only button, a status marker. Omit it for decoration next to a visible
|
|
53
|
+
* label, and the icon is hidden from assistive tech instead of read out twice.
|
|
54
|
+
*/
|
|
55
|
+
label?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Wrapper that applies the design system's icon sizing and accessibility
|
|
59
|
+
* defaults to whatever icon component you hand it.
|
|
60
|
+
*
|
|
61
|
+
* import { Plus } from 'lucide-react';
|
|
62
|
+
* <Icon as={Plus} size="sm" />
|
|
63
|
+
* <Icon as={Plus} label="Add item" /> // meaningful, gets an a11y name
|
|
64
|
+
*
|
|
65
|
+
* Takes the icon as a prop rather than re-exporting a set: a barrel of a
|
|
66
|
+
* thousand-plus icons defeats tree-shaking in several bundlers, and pinning one
|
|
67
|
+
* icon library would force it on every consumer. You import the one icon you
|
|
68
|
+
* need, from whichever library you use, and it is the only one bundled.
|
|
69
|
+
*/
|
|
70
|
+
export declare const Icon: React.ForwardRefExoticComponent<IconProps & React.RefAttributes<SVGSVGElement>>;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=Icon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Icon.d.ts","sourceRoot":"","sources":["../src/Icon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,CAC7C,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,CACnE,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,WAAW;;;;;;CAMP,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,WAAW,CAAC;AAEhD,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC;IAC3E;;;OAGG;IACH,EAAE,EAAE,aAAa,CAAC;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAChC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,IAAI,iFAqBhB,CAAC"}
|
package/dist/Icon.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* The `icon-size` ladder, verbatim.
|
|
5
|
+
*
|
|
6
|
+
* These map one-to-one onto the Semantics tokens `icon-size/xs...xl`, so `md`
|
|
7
|
+
* here and `icon-size/md` in Figma are the same 20px. There is no separate
|
|
8
|
+
* decision being made in this file.
|
|
9
|
+
*
|
|
10
|
+
* BREAKING, AND DELIBERATELY SO
|
|
11
|
+
*
|
|
12
|
+
* This used to be two sizes where `sm` was 16 and `md` was **24**. That made
|
|
13
|
+
* `md` mean `icon-size/lg`, so the one word meant two different things
|
|
14
|
+
* depending on whether you were reading code or Figma — the exact ambiguity the
|
|
15
|
+
* ladder exists to remove. Callers who want the old `md` now ask for `lg`.
|
|
16
|
+
*
|
|
17
|
+
* Nothing inside the system depended on it: Button and Tabs size their icons in
|
|
18
|
+
* CSS from `--icon-size-*`, never through this prop.
|
|
19
|
+
*/
|
|
20
|
+
const SIZE_TOKENS = {
|
|
21
|
+
xs: 'var(--icon-size-xs)',
|
|
22
|
+
sm: 'var(--icon-size-sm)',
|
|
23
|
+
md: 'var(--icon-size-md)',
|
|
24
|
+
lg: 'var(--icon-size-lg)',
|
|
25
|
+
xl: 'var(--icon-size-xl)',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Wrapper that applies the design system's icon sizing and accessibility
|
|
29
|
+
* defaults to whatever icon component you hand it.
|
|
30
|
+
*
|
|
31
|
+
* import { Plus } from 'lucide-react';
|
|
32
|
+
* <Icon as={Plus} size="sm" />
|
|
33
|
+
* <Icon as={Plus} label="Add item" /> // meaningful, gets an a11y name
|
|
34
|
+
*
|
|
35
|
+
* Takes the icon as a prop rather than re-exporting a set: a barrel of a
|
|
36
|
+
* thousand-plus icons defeats tree-shaking in several bundlers, and pinning one
|
|
37
|
+
* icon library would force it on every consumer. You import the one icon you
|
|
38
|
+
* need, from whichever library you use, and it is the only one bundled.
|
|
39
|
+
*/
|
|
40
|
+
export const Icon = forwardRef(({ as: Component, size, label, className, ...rest }, ref) => {
|
|
41
|
+
const resolved = size === undefined ? '1em' : (SIZE_TOKENS[size] ?? size);
|
|
42
|
+
return (_jsx(Component, { ref: ref, width: resolved, height: resolved,
|
|
43
|
+
// Lucide strokes scale with the box; currentColor keeps the icon tied to
|
|
44
|
+
// whatever text colour it sits in, so it themes for free.
|
|
45
|
+
color: "currentColor", className: ['ion-icon', className].filter(Boolean).join(' '), ...(label
|
|
46
|
+
? { role: 'img', 'aria-label': label }
|
|
47
|
+
: { 'aria-hidden': true, focusable: false }), ...rest }));
|
|
48
|
+
});
|
|
49
|
+
Icon.displayName = 'Icon';
|
|
50
|
+
//# sourceMappingURL=Icon.js.map
|
package/dist/Icon.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Icon.js","sourceRoot":"","sources":["../src/Icon.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAe1C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,qBAAqB;IACzB,EAAE,EAAE,qBAAqB;IACzB,EAAE,EAAE,qBAAqB;IACzB,EAAE,EAAE,qBAAqB;IACzB,EAAE,EAAE,qBAAqB;CACjB,CAAC;AAyBX;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAC5B,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE;IAC1D,MAAM,QAAQ,GACZ,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAgB,CAAC,IAAI,IAAI,CAAC,CAAC;IAEvE,OAAO,CACL,KAAC,SAAS,IACR,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,QAAQ;QAChB,yEAAyE;QACzE,0DAA0D;QAC1D,KAAK,EAAC,cAAc,EACpB,SAAS,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KACxD,CAAC,KAAK;YACR,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE;YACtC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,KAC1C,IAAI,GACR,CACH,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type AvatarSize = 'mini' | 'sm' | 'md' | 'lg';
|
|
3
|
+
export type AvatarShape = 'circle' | 'square';
|
|
4
|
+
export interface AvatarProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
5
|
+
/** Matches the Figma `Size` variant: Mini 24, Small 32, Medium 40, Large 48. */
|
|
6
|
+
size?: AvatarSize;
|
|
7
|
+
/** Matches the Figma `Shape` variant. */
|
|
8
|
+
shape?: AvatarShape;
|
|
9
|
+
/** Image source. Figma's `Type=Image`. */
|
|
10
|
+
src?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Describes the person, not the picture. Falls back to `initials` so the
|
|
13
|
+
* avatar is never announced as an unlabelled image.
|
|
14
|
+
*/
|
|
15
|
+
alt?: string;
|
|
16
|
+
/** Figma's `Type=Character`. Rendered when there is no `src`. */
|
|
17
|
+
initials?: string;
|
|
18
|
+
/** Figma's `Type=Icon`. Rendered when there is neither `src` nor `initials`. */
|
|
19
|
+
icon?: React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Avatar picks its content the way Figma's `Type` variant does, but by
|
|
23
|
+
* precedence rather than by a prop: image, then initials, then icon. A caller
|
|
24
|
+
* passing `src` and `initials` gets the image with the initials as its alt
|
|
25
|
+
* fallback, which is what you want when the image 404s.
|
|
26
|
+
*
|
|
27
|
+
* It is a `<span>`, not a `<div>`, so it can sit inline beside text without the
|
|
28
|
+
* caller fighting a block element.
|
|
29
|
+
*/
|
|
30
|
+
export declare const Avatar: React.ForwardRefExoticComponent<AvatarProps & React.RefAttributes<HTMLSpanElement>>;
|
|
31
|
+
export interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
32
|
+
size?: AvatarSize;
|
|
33
|
+
/** Cap on avatars shown; the remainder becomes a `+N` overflow avatar. */
|
|
34
|
+
max?: number;
|
|
35
|
+
shape?: AvatarShape;
|
|
36
|
+
children?: React.ReactNode;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* AvatarGroup overlaps its children by a quarter of the avatar size, matching
|
|
40
|
+
* Figma's -6 / -8 / -10 / -12 gaps at Mini / Small / Medium / Large.
|
|
41
|
+
*
|
|
42
|
+
* It passes `size` and `shape` down rather than trusting the caller to set them
|
|
43
|
+
* on every child: the overlap is computed from the size, so a mismatched child
|
|
44
|
+
* would overlap by the wrong amount and the error would look like a design bug
|
|
45
|
+
* rather than a wiring one.
|
|
46
|
+
*
|
|
47
|
+
* Children render in source order because that is Figma's stacking: each avatar
|
|
48
|
+
* paints over the one before it, so the `+N` overflow ends up on top. Later
|
|
49
|
+
* siblings paint later, so this needs no z-index.
|
|
50
|
+
*/
|
|
51
|
+
export declare const AvatarGroup: React.ForwardRefExoticComponent<AvatarGroupProps & React.RefAttributes<HTMLDivElement>>;
|
|
52
|
+
//# sourceMappingURL=Avatar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../src/components/Avatar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACrD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,WAAY,SAAQ,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC;IACxE,gFAAgF;IAChF,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,yCAAyC;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,qFA8ClB,CAAC;AAIF,MAAM,WAAW,gBAAiB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC5E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,yFA6CvB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { forwardRef } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Avatar picks its content the way Figma's `Type` variant does, but by
|
|
5
|
+
* precedence rather than by a prop: image, then initials, then icon. A caller
|
|
6
|
+
* passing `src` and `initials` gets the image with the initials as its alt
|
|
7
|
+
* fallback, which is what you want when the image 404s.
|
|
8
|
+
*
|
|
9
|
+
* It is a `<span>`, not a `<div>`, so it can sit inline beside text without the
|
|
10
|
+
* caller fighting a block element.
|
|
11
|
+
*/
|
|
12
|
+
export const Avatar = forwardRef(({ size = 'md', shape = 'circle', src, alt, initials, icon, className, ...rest }, ref) => {
|
|
13
|
+
const classNames = [
|
|
14
|
+
'ion-avatar',
|
|
15
|
+
size !== 'md' ? `ion-avatar--${size}` : '',
|
|
16
|
+
shape === 'circle' ? 'ion-avatar--circle' : '',
|
|
17
|
+
className || '',
|
|
18
|
+
]
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join(' ');
|
|
21
|
+
const label = alt ?? initials;
|
|
22
|
+
return (_jsx("span", { ...rest, ref: ref, className: classNames, ...(!src && label ? { role: 'img', 'aria-label': label } : {}), children: src ? (_jsx("img", { className: "ion-avatar__image", src: src, alt: label ?? '' })) : initials ? (_jsx("span", { "aria-hidden": "true", children: initials })) : icon ? (_jsx("span", { className: "ion-avatar__icon", "aria-hidden": "true", children: icon })) : null }));
|
|
23
|
+
});
|
|
24
|
+
Avatar.displayName = 'Avatar';
|
|
25
|
+
/**
|
|
26
|
+
* AvatarGroup overlaps its children by a quarter of the avatar size, matching
|
|
27
|
+
* Figma's -6 / -8 / -10 / -12 gaps at Mini / Small / Medium / Large.
|
|
28
|
+
*
|
|
29
|
+
* It passes `size` and `shape` down rather than trusting the caller to set them
|
|
30
|
+
* on every child: the overlap is computed from the size, so a mismatched child
|
|
31
|
+
* would overlap by the wrong amount and the error would look like a design bug
|
|
32
|
+
* rather than a wiring one.
|
|
33
|
+
*
|
|
34
|
+
* Children render in source order because that is Figma's stacking: each avatar
|
|
35
|
+
* paints over the one before it, so the `+N` overflow ends up on top. Later
|
|
36
|
+
* siblings paint later, so this needs no z-index.
|
|
37
|
+
*/
|
|
38
|
+
export const AvatarGroup = forwardRef(({ size = 'md', shape = 'circle', max, className, children, ...rest }, ref) => {
|
|
39
|
+
const items = React.Children.toArray(children).filter(React.isValidElement);
|
|
40
|
+
const shown = max !== undefined ? items.slice(0, max) : items;
|
|
41
|
+
const overflow = items.length - shown.length;
|
|
42
|
+
const sized = shown.map((child, i) => React.cloneElement(child, {
|
|
43
|
+
size,
|
|
44
|
+
shape,
|
|
45
|
+
key: child.key ?? i,
|
|
46
|
+
}));
|
|
47
|
+
if (overflow > 0) {
|
|
48
|
+
sized.push(_jsx(Avatar, { size: size, shape: shape, initials: `+${overflow}`, alt: `${overflow} more` }, "__overflow"));
|
|
49
|
+
}
|
|
50
|
+
return (_jsx("div", { ...rest, ref: ref, className: [
|
|
51
|
+
'ion-avatar-group',
|
|
52
|
+
`ion-avatar-group--${size}`,
|
|
53
|
+
className || '',
|
|
54
|
+
]
|
|
55
|
+
.filter(Boolean)
|
|
56
|
+
.join(' '), children: sized }));
|
|
57
|
+
});
|
|
58
|
+
AvatarGroup.displayName = 'AvatarGroup';
|
|
59
|
+
//# sourceMappingURL=Avatar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Avatar.js","sourceRoot":"","sources":["../../src/components/Avatar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type BadgeIntent = 'neutral' | 'primary' | 'success' | 'warning' | 'error' | 'information';
|
|
3
|
+
export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
4
|
+
/** Matches the Figma `Intent` variant. */
|
|
5
|
+
intent?: BadgeIntent;
|
|
6
|
+
/**
|
|
7
|
+
* Show the leading dot marker. Figma's `Show Dot` boolean.
|
|
8
|
+
* The dot inherits the intent's foreground colour, so it needs no prop.
|
|
9
|
+
*/
|
|
10
|
+
dot?: boolean;
|
|
11
|
+
/** Leading icon. Figma's `Show Icon` + `Icon` swap. Mutually exclusive with `dot`. */
|
|
12
|
+
icon?: React.ReactNode;
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Badge is presentational — no role, no interaction. React Aria has nothing to
|
|
17
|
+
* offer here, so this is a plain span rather than a hook wrapper for its own
|
|
18
|
+
* sake.
|
|
19
|
+
*
|
|
20
|
+
* It carries no `status` or `alert` role on purpose: a badge is a label on
|
|
21
|
+
* something else, and announcing it as a live region would interrupt screen
|
|
22
|
+
* reader users on every render. Wrap it yourself if the value genuinely changes
|
|
23
|
+
* and matters.
|
|
24
|
+
*/
|
|
25
|
+
export declare const Badge: React.ForwardRefExoticComponent<BadgeProps & React.RefAttributes<HTMLSpanElement>>;
|
|
26
|
+
//# sourceMappingURL=Badge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../src/components/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C,MAAM,MAAM,WAAW,GACrB,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAE1E,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC;IACvE,0CAA0C;IAC1C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,sFAAsF;IACtF,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,oFAcjB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Badge is presentational — no role, no interaction. React Aria has nothing to
|
|
5
|
+
* offer here, so this is a plain span rather than a hook wrapper for its own
|
|
6
|
+
* sake.
|
|
7
|
+
*
|
|
8
|
+
* It carries no `status` or `alert` role on purpose: a badge is a label on
|
|
9
|
+
* something else, and announcing it as a live region would interrupt screen
|
|
10
|
+
* reader users on every render. Wrap it yourself if the value genuinely changes
|
|
11
|
+
* and matters.
|
|
12
|
+
*/
|
|
13
|
+
export const Badge = forwardRef(({ intent = 'neutral', dot, icon, className, children, ...rest }, ref) => (_jsxs("span", { ...rest, ref: ref, className: ['ion-badge', `ion-badge--${intent}`, className]
|
|
14
|
+
.filter(Boolean)
|
|
15
|
+
.join(' '), children: [dot && _jsx("span", { className: "ion-badge__dot", "aria-hidden": "true" }), !dot && icon, children] })));
|
|
16
|
+
Badge.displayName = 'Badge';
|
|
17
|
+
//# sourceMappingURL=Badge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Badge.js","sourceRoot":"","sources":["../../src/components/Badge.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAkB1C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAC7B,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CACxE,mBACM,IAAI,EACR,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,CAAC,WAAW,EAAE,cAAc,MAAM,EAAE,EAAE,SAAS,CAAC;SACxD,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,aAEX,GAAG,IAAI,eAAM,SAAS,EAAC,gBAAgB,iBAAa,MAAM,GAAG,EAC7D,CAAC,GAAG,IAAI,IAAI,EACZ,QAAQ,IACJ,CACR,CACF,CAAC;AAEF,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type AriaButtonProps } from 'react-aria';
|
|
3
|
+
export interface ButtonProps extends AriaButtonProps<'button'> {
|
|
4
|
+
/** The visual style variant of the button. */
|
|
5
|
+
variant?: 'primary-brand' | 'primary-neutral' | 'secondary' | 'tertiary' | 'destructive';
|
|
6
|
+
/** The size of the button. */
|
|
7
|
+
size?: 'sm' | 'md' | 'lg';
|
|
8
|
+
/** Optional icon to render before the label. */
|
|
9
|
+
startIcon?: React.ReactNode;
|
|
10
|
+
/** Optional icon to render after the label. */
|
|
11
|
+
endIcon?: React.ReactNode;
|
|
12
|
+
/** Additional CSS class names. */
|
|
13
|
+
className?: string;
|
|
14
|
+
/** Children element to render inside the button. */
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
18
|
+
//# sourceMappingURL=Button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/components/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkD,MAAM,OAAO,CAAC;AACvE,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,WAAY,SAAQ,eAAe,CAAC,QAAQ,CAAC;IAC5D,8CAA8C;IAC9C,OAAO,CAAC,EACJ,eAAe,GACf,iBAAiB,GACjB,WAAW,GACX,UAAU,GACV,aAAa,CAAC;IAClB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,gDAAgD;IAChD,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,eAAO,MAAM,MAAM,uFAoElB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useRef, useImperativeHandle } from 'react';
|
|
3
|
+
import { useButton, useHover, useFocusRing, mergeProps, } from 'react-aria';
|
|
4
|
+
export const Button = forwardRef((props, forwardedRef) => {
|
|
5
|
+
const { variant = 'primary-brand', size = 'md', startIcon, endIcon, className: customClassName, children, isDisabled, ...restProps } = props;
|
|
6
|
+
const domRef = useRef(null);
|
|
7
|
+
useImperativeHandle(forwardedRef, () => domRef.current);
|
|
8
|
+
const { buttonProps, isPressed } = useButton(props, domRef);
|
|
9
|
+
/*
|
|
10
|
+
* Interaction state comes from React Aria rather than CSS pseudo-classes.
|
|
11
|
+
*
|
|
12
|
+
* `useHover` is pointer-aware: native `:hover` latches on touch devices, so
|
|
13
|
+
* a tapped button stays visually hovered until you tap elsewhere.
|
|
14
|
+
* `useFocusRing` distinguishes keyboard focus from a mouse click, so the
|
|
15
|
+
* ring appears when navigating but not when clicking.
|
|
16
|
+
*
|
|
17
|
+
* The CSS keeps its `:hover` / `:focus-visible` rules so the stylesheet
|
|
18
|
+
* still works without React — these attributes refine that, they do not
|
|
19
|
+
* replace it.
|
|
20
|
+
*/
|
|
21
|
+
const { hoverProps, isHovered } = useHover({ isDisabled });
|
|
22
|
+
const { focusProps, isFocusVisible } = useFocusRing();
|
|
23
|
+
const classNames = [
|
|
24
|
+
'ion-button',
|
|
25
|
+
`ion-button--${variant}`,
|
|
26
|
+
size !== 'md' ? `ion-button--${size}` : '',
|
|
27
|
+
customClassName || '',
|
|
28
|
+
]
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.join(' ');
|
|
31
|
+
// `undefined` rather than `false` so the attribute is omitted entirely
|
|
32
|
+
// instead of rendering data-hovered="false", which would still match
|
|
33
|
+
// an `[data-hovered]` selector.
|
|
34
|
+
const stateAttributes = {
|
|
35
|
+
'data-hovered': isHovered || undefined,
|
|
36
|
+
'data-pressed': isPressed || undefined,
|
|
37
|
+
'data-focused': isFocusVisible || undefined,
|
|
38
|
+
'data-disabled': isDisabled || undefined,
|
|
39
|
+
};
|
|
40
|
+
return (_jsxs("button", { ...restProps, ...mergeProps(buttonProps, hoverProps, focusProps), ...stateAttributes, ref: domRef, className: classNames, children: [startIcon && (_jsx("span", { className: "ion-button__icon-start", children: startIcon })), children && _jsx("span", { className: "ion-button__label", children: children }), endIcon && _jsx("span", { className: "ion-button__icon-end", children: endIcon })] }));
|
|
41
|
+
});
|
|
42
|
+
Button.displayName = 'Button';
|
|
43
|
+
//# sourceMappingURL=Button.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.js","sourceRoot":"","sources":["../../src/components/Button.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AACvE,OAAO,EACL,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,UAAU,GAEX,MAAM,YAAY,CAAC;AAsBpB,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAC9B,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE;IACtB,MAAM,EACJ,OAAO,GAAG,eAAe,EACzB,IAAI,GAAG,IAAI,EACX,SAAS,EACT,OAAO,EACP,SAAS,EAAE,eAAe,EAC1B,QAAQ,EACR,UAAU,EACV,GAAG,SAAS,EACb,GAAG,KAAK,CAAC;IAEV,MAAM,MAAM,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAC;IAC/C,mBAAmB,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAQ,CAAC,CAAC;IAEzD,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAE5D;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;IAC3D,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,YAAY,EAAE,CAAC;IAEtD,MAAM,UAAU,GAAG;QACjB,YAAY;QACZ,eAAe,OAAO,EAAE;QACxB,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;QAC1C,eAAe,IAAI,EAAE;KACtB;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,uEAAuE;IACvE,qEAAqE;IACrE,gCAAgC;IAChC,MAAM,eAAe,GAAG;QACtB,cAAc,EAAE,SAAS,IAAI,SAAS;QACtC,cAAc,EAAE,SAAS,IAAI,SAAS;QACtC,cAAc,EAAE,cAAc,IAAI,SAAS;QAC3C,eAAe,EAAE,UAAU,IAAI,SAAS;KACzC,CAAC;IAEF,OAAO,CACL,qBACM,SAAS,KACT,UAAU,CAAC,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC,KAC/C,eAAe,EACnB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,UAAU,aAEpB,SAAS,IAAI,CACZ,eAAM,SAAS,EAAC,wBAAwB,YAAE,SAAS,GAAQ,CAC5D,EACA,QAAQ,IAAI,eAAM,SAAS,EAAC,mBAAmB,YAAE,QAAQ,GAAQ,EACjE,OAAO,IAAI,eAAM,SAAS,EAAC,sBAAsB,YAAE,OAAO,GAAQ,IAC5D,CACV,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type CheckboxSize = 'sm' | 'md' | 'lg';
|
|
3
|
+
export type CheckboxIntent = 'brand' | 'neutral' | 'danger';
|
|
4
|
+
export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'type'> {
|
|
5
|
+
/** Matches the Figma `Size` variant: Small, Medium, Large. */
|
|
6
|
+
size?: CheckboxSize;
|
|
7
|
+
/** Matches the Figma `Color` variant: Brand, Neutral, Danger. */
|
|
8
|
+
intent?: CheckboxIntent;
|
|
9
|
+
/**
|
|
10
|
+
* Figma's `Indeterminate` state. Not an HTML attribute — `indeterminate` is a
|
|
11
|
+
* DOM property only, so it has to be assigned after render.
|
|
12
|
+
*/
|
|
13
|
+
isIndeterminate?: boolean;
|
|
14
|
+
/** Figma's `Show Label` + `Label`. Omit for a bare box. */
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Checkbox wraps a native `<input type="checkbox">` and hides it.
|
|
19
|
+
*
|
|
20
|
+
* The native input is kept rather than replaced by a div with `role=checkbox`
|
|
21
|
+
* because it brings form association, the indeterminate property, label
|
|
22
|
+
* clicking, and correct announcement — none of which are free to reimplement,
|
|
23
|
+
* and all of which are easy to get subtly wrong.
|
|
24
|
+
*
|
|
25
|
+
* `indeterminate` is the reason for the effect below: HTML has no
|
|
26
|
+
* `indeterminate` attribute, only a DOM property, so React cannot set it
|
|
27
|
+
* declaratively and it must be written after every render.
|
|
28
|
+
*/
|
|
29
|
+
export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>>;
|
|
30
|
+
//# sourceMappingURL=Checkbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../src/components/Checkbox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAKN,MAAM,OAAO,CAAC;AAEf,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC9C,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE5D,MAAM,WAAW,aAAc,SAAQ,IAAI,CACzC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,MAAM,GAAG,MAAM,CAChB;IACC,8DAA8D;IAC9D,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,iEAAiE;IACjE,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AA2BD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,QAAQ,wFA+CpB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useRef, useImperativeHandle, useEffect, } from 'react';
|
|
3
|
+
/** Figma's check glyph. Inlined for the same reason as Select's chevron: it is
|
|
4
|
+
* part of the control, not a slot a caller fills. */
|
|
5
|
+
const CheckMark = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "m5 13 4 4L19 7", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
6
|
+
const DashMark = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M6 12h12", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round" }) }));
|
|
7
|
+
/**
|
|
8
|
+
* Checkbox wraps a native `<input type="checkbox">` and hides it.
|
|
9
|
+
*
|
|
10
|
+
* The native input is kept rather than replaced by a div with `role=checkbox`
|
|
11
|
+
* because it brings form association, the indeterminate property, label
|
|
12
|
+
* clicking, and correct announcement — none of which are free to reimplement,
|
|
13
|
+
* and all of which are easy to get subtly wrong.
|
|
14
|
+
*
|
|
15
|
+
* `indeterminate` is the reason for the effect below: HTML has no
|
|
16
|
+
* `indeterminate` attribute, only a DOM property, so React cannot set it
|
|
17
|
+
* declaratively and it must be written after every render.
|
|
18
|
+
*/
|
|
19
|
+
export const Checkbox = forwardRef((props, forwardedRef) => {
|
|
20
|
+
const { size = 'md', intent = 'brand', isIndeterminate = false, className, children, disabled, ...rest } = props;
|
|
21
|
+
const domRef = useRef(null);
|
|
22
|
+
useImperativeHandle(forwardedRef, () => domRef.current);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (domRef.current)
|
|
25
|
+
domRef.current.indeterminate = isIndeterminate;
|
|
26
|
+
}, [isIndeterminate]);
|
|
27
|
+
const classNames = [
|
|
28
|
+
'ion-checkbox',
|
|
29
|
+
size !== 'md' ? `ion-checkbox--${size}` : '',
|
|
30
|
+
intent !== 'brand' ? `ion-checkbox--${intent}` : '',
|
|
31
|
+
disabled ? 'ion-checkbox--disabled' : '',
|
|
32
|
+
className || '',
|
|
33
|
+
]
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.join(' ');
|
|
36
|
+
return (_jsxs("label", { className: classNames, children: [_jsx("input", { ...rest, ref: domRef, type: "checkbox", disabled: disabled, className: "ion-checkbox__input" }), _jsx("span", { className: "ion-checkbox__indicator", "aria-hidden": "true", children: _jsx("span", { className: "ion-checkbox__mark", children: isIndeterminate ? _jsx(DashMark, {}) : _jsx(CheckMark, {}) }) }), children && _jsx("span", { className: "ion-checkbox__label", children: children })] }));
|
|
37
|
+
});
|
|
38
|
+
Checkbox.displayName = 'Checkbox';
|
|
39
|
+
//# sourceMappingURL=Checkbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/components/Checkbox.tsx"],"names":[],"mappings":";AAAA,OAAc,EACZ,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,SAAS,GACV,MAAM,OAAO,CAAC;AAsBf;sDACsD;AACtD,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CACtB,cAAK,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,iBAAa,MAAM,EAAC,SAAS,EAAC,OAAO,YACvE,eACE,CAAC,EAAC,gBAAgB,EAClB,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,GACtB,GACE,CACP,CAAC;AAEF,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,CACrB,cAAK,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,iBAAa,MAAM,EAAC,SAAS,EAAC,OAAO,YACvE,eACE,CAAC,EAAC,UAAU,EACZ,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,GACrB,GACE,CACP,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAChC,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE;IACtB,MAAM,EACJ,IAAI,GAAG,IAAI,EACX,MAAM,GAAG,OAAO,EAChB,eAAe,GAAG,KAAK,EACvB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,GAAG,KAAK,CAAC;IAEV,MAAM,MAAM,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAC9C,mBAAmB,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAQ,CAAC,CAAC;IAEzD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,CAAC,OAAO;YAAE,MAAM,CAAC,OAAO,CAAC,aAAa,GAAG,eAAe,CAAC;IACrE,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,MAAM,UAAU,GAAG;QACjB,cAAc;QACd,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;QAC5C,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,iBAAiB,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE;QACnD,QAAQ,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE;QACxC,SAAS,IAAI,EAAE;KAChB;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,OAAO,CACL,iBAAO,SAAS,EAAE,UAAU,aAC1B,mBACM,IAAI,EACR,GAAG,EAAE,MAAM,EACX,IAAI,EAAC,UAAU,EACf,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAC,qBAAqB,GAC/B,EACF,eAAM,SAAS,EAAC,yBAAyB,iBAAa,MAAM,YAC1D,eAAM,SAAS,EAAC,oBAAoB,YACjC,eAAe,CAAC,CAAC,CAAC,KAAC,QAAQ,KAAG,CAAC,CAAC,CAAC,KAAC,SAAS,KAAG,GAC1C,GACF,EACN,QAAQ,IAAI,eAAM,SAAS,EAAC,qBAAqB,YAAE,QAAQ,GAAQ,IAC9D,CACT,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type DividerOrientation = 'horizontal' | 'vertical';
|
|
3
|
+
export interface DividerProps extends React.HTMLAttributes<HTMLHRElement> {
|
|
4
|
+
/** Matches Figma's `Style` variant. */
|
|
5
|
+
orientation?: DividerOrientation;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A single `<hr>`, matching Figma's `Border` (70:22153).
|
|
9
|
+
*
|
|
10
|
+
* `<hr>` rather than a styled `<div>`: it is a semantic thematic break, so a
|
|
11
|
+
* screen reader announces it as one, and it needs no `role` to get there.
|
|
12
|
+
* `aria-orientation` is set for the vertical case, since a vertical rule
|
|
13
|
+
* inside a horizontal toolbar is the one shape a screen reader cannot infer
|
|
14
|
+
* from the element alone.
|
|
15
|
+
*/
|
|
16
|
+
export declare const Divider: React.ForwardRefExoticComponent<DividerProps & React.RefAttributes<HTMLHRElement>>;
|
|
17
|
+
//# sourceMappingURL=Divider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../../src/components/Divider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,UAAU,CAAC;AAE3D,MAAM,WAAW,YAAa,SAAQ,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC;IACvE,uCAAuC;IACvC,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,oFAWnB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* A single `<hr>`, matching Figma's `Border` (70:22153).
|
|
5
|
+
*
|
|
6
|
+
* `<hr>` rather than a styled `<div>`: it is a semantic thematic break, so a
|
|
7
|
+
* screen reader announces it as one, and it needs no `role` to get there.
|
|
8
|
+
* `aria-orientation` is set for the vertical case, since a vertical rule
|
|
9
|
+
* inside a horizontal toolbar is the one shape a screen reader cannot infer
|
|
10
|
+
* from the element alone.
|
|
11
|
+
*/
|
|
12
|
+
export const Divider = forwardRef(({ orientation = 'horizontal', className, ...rest }, ref) => (_jsx("hr", { ...rest, ref: ref, "aria-orientation": orientation === 'vertical' ? 'vertical' : undefined, className: ['ion-divider', `ion-divider--${orientation}`, className]
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
.join(' ') })));
|
|
15
|
+
Divider.displayName = 'Divider';
|
|
16
|
+
//# sourceMappingURL=Divider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Divider.js","sourceRoot":"","sources":["../../src/components/Divider.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAS1C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAC/B,CAAC,EAAE,WAAW,GAAG,YAAY,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAC3D,gBACM,IAAI,EACR,GAAG,EAAE,GAAG,sBACU,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EACrE,SAAS,EAAE,CAAC,aAAa,EAAE,gBAAgB,WAAW,EAAE,EAAE,SAAS,CAAC;SACjE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,GACZ,CACH,CACF,CAAC;AAEF,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface HeaderProps extends React.HTMLAttributes<HTMLElement> {
|
|
3
|
+
/** Figma's Logo slot. Rendered at the start, never shrinks. */
|
|
4
|
+
brand?: React.ReactNode;
|
|
5
|
+
/** Figma's `Center Slot`. Hidden below 1216px, as in the Tablet and Mobile variants. */
|
|
6
|
+
center?: React.ReactNode;
|
|
7
|
+
/** Figma's `End Slot`. */
|
|
8
|
+
end?: React.ReactNode;
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Header renders Figma's three Device variants through media queries, not a
|
|
13
|
+
* prop.
|
|
14
|
+
*
|
|
15
|
+
* Device is the one variant axis in the system the browser already knows the
|
|
16
|
+
* answer to. Every other component takes its variant from the caller because
|
|
17
|
+
* the caller is the only one who knows; a header is Mobile because the viewport
|
|
18
|
+
* is narrow, and having React duplicate that judgement is how the two drift
|
|
19
|
+
* apart. Breakpoints match the Breakpoint collection's container widths.
|
|
20
|
+
*
|
|
21
|
+
* A `<header>` element with no explicit role: it is a landmark already when it
|
|
22
|
+
* is a direct child of body, which is where a page header sits.
|
|
23
|
+
*/
|
|
24
|
+
export declare const Header: React.ForwardRefExoticComponent<HeaderProps & React.RefAttributes<HTMLElement>>;
|
|
25
|
+
//# sourceMappingURL=Header.d.ts.map
|