@oneli8/react 1.0.0-beta.10
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/AGENTS.md +16 -0
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/PRINCIPLES.md +48 -0
- package/README.md +136 -0
- package/ai-context.json +264 -0
- package/package.json +66 -0
- package/skills/oneli8-figma-to-code/SKILL.md +25 -0
- package/skills/oneli8-icons/SKILL.md +36 -0
- package/skills/oneli8-ui/SKILL.md +52 -0
- package/src/atoms/Icon.js +34 -0
- package/src/atoms/NavigationBadge.js +19 -0
- package/src/atoms/SelectionIndicator.js +43 -0
- package/src/foundations/geometry.js +143 -0
- package/src/foundations/icons.generated.d.ts +48 -0
- package/src/foundations/icons.generated.js +562 -0
- package/src/index.d.ts +381 -0
- package/src/index.js +23 -0
- package/src/molecules/Button.js +50 -0
- package/src/molecules/ChoiceChip.js +101 -0
- package/src/molecules/ChoiceItem.js +55 -0
- package/src/molecules/FormMessage.js +33 -0
- package/src/molecules/IconButton.js +33 -0
- package/src/molecules/Link.js +31 -0
- package/src/molecules/NavigationItem.js +55 -0
- package/src/molecules/Select.js +109 -0
- package/src/molecules/SelectionOption.js +44 -0
- package/src/molecules/TextField.js +174 -0
- package/src/molecules/Token.js +49 -0
- package/src/organisms/ChoiceGroup.js +78 -0
- package/src/organisms/ChoicePicker.js +114 -0
- package/src/organisms/Combobox.js +122 -0
- package/src/organisms/MultiSelectField.js +138 -0
- package/src/organisms/SegmentedControl.js +114 -0
- package/src/organisms/SelectionPopup.js +73 -0
- package/src/organisms/TabBar.js +68 -0
- package/src/organisms/Tabs.js +123 -0
- package/src/styles/button.css +195 -0
- package/src/styles/choice-chip.css +216 -0
- package/src/styles/choice-group.css +95 -0
- package/src/styles/choice-item.css +267 -0
- package/src/styles/choice-picker.css +100 -0
- package/src/styles/combobox.css +42 -0
- package/src/styles/form-message.css +48 -0
- package/src/styles/gem.css +376 -0
- package/src/styles/icon-button.css +224 -0
- package/src/styles/icon.css +56 -0
- package/src/styles/link.css +116 -0
- package/src/styles/multi-select-field.css +78 -0
- package/src/styles/navigation-badge.css +37 -0
- package/src/styles/navigation-item.css +155 -0
- package/src/styles/segmented-control.css +115 -0
- package/src/styles/select.css +33 -0
- package/src/styles/selection-indicator.css +162 -0
- package/src/styles/selection-option.css +123 -0
- package/src/styles/selection-popup.css +92 -0
- package/src/styles/tab-bar.css +47 -0
- package/src/styles/tabs.css +55 -0
- package/src/styles/text-field.css +268 -0
- package/src/styles/token.css +65 -0
- package/styles.css +29 -0
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@oneli8/react",
|
|
3
|
+
"version": "1.0.0-beta.10",
|
|
4
|
+
"description": "OneLi8 Design System for React. Components, design tokens, the Gem material, and the design language they follow.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"design-system",
|
|
8
|
+
"components",
|
|
9
|
+
"design-tokens",
|
|
10
|
+
"accessibility",
|
|
11
|
+
"atomic-design",
|
|
12
|
+
"oneli8"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/Akshay007ad/OneLi8-Design-System#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/Akshay007ad/OneLi8-Design-System/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/Akshay007ad/OneLi8-Design-System.git",
|
|
21
|
+
"directory": "packages/react"
|
|
22
|
+
},
|
|
23
|
+
"license": "Apache-2.0",
|
|
24
|
+
"author": "Akshay Dhore",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./src/index.d.ts",
|
|
29
|
+
"import": "./src/index.js",
|
|
30
|
+
"default": "./src/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./styles.css": "./styles.css",
|
|
33
|
+
"./styles/*.css": "./src/styles/*.css",
|
|
34
|
+
"./ai-context.json": "./ai-context.json",
|
|
35
|
+
"./skills/*": "./skills/*",
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"types": "./src/index.d.ts",
|
|
39
|
+
"sideEffects": [
|
|
40
|
+
"./styles.css",
|
|
41
|
+
"**/*.css"
|
|
42
|
+
],
|
|
43
|
+
"files": [
|
|
44
|
+
"src",
|
|
45
|
+
"styles.css",
|
|
46
|
+
"ai-context.json",
|
|
47
|
+
"skills",
|
|
48
|
+
"PRINCIPLES.md",
|
|
49
|
+
"AGENTS.md",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE",
|
|
52
|
+
"NOTICE"
|
|
53
|
+
],
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@oneli8/tokens": "1.0.0-beta.10"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"react": ">=18"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=20"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oneli8-figma-to-code
|
|
3
|
+
description: Translate inspected OneLi8 Button, IconButton and Link properties into canonical code without paid Code Connect.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Locate the bundle; read README.md, PRINCIPLES.md and the matching JSON and module
|
|
7
|
+
in mappings/. These are property translators, not a Figma exporter. They need no
|
|
8
|
+
paid Code Connect service. They do not supply a consumer's Figma access.
|
|
9
|
+
|
|
10
|
+
Use an authorized connector, user-run read-only plugin, or manually supplied
|
|
11
|
+
observations to obtain owner identity, exact properties, nested icon identities
|
|
12
|
+
and token bindings. Never require enterprise Variables REST access. Screenshots
|
|
13
|
+
alone support provisional visual work, not structural certification.
|
|
14
|
+
|
|
15
|
+
Call mapButton, mapIconButton or mapLink with the shipped mapping and explicit
|
|
16
|
+
observations; use the function signature in its module. Block unknown properties
|
|
17
|
+
and missing identities. Copied libraries may change IDs: inspect rather than
|
|
18
|
+
silently guessing. Do not build replacement HTML from a screenshot when a public
|
|
19
|
+
owner exists. No component outside the public beta is supported by these adapters.
|
|
20
|
+
|
|
21
|
+
Preserve approved Figma and decisions. Code mapping does not authorize changing
|
|
22
|
+
Figma. Map focus/hover to real browser interactions, not invented public props.
|
|
23
|
+
Verify properties, structural/token ownership, matched visual states, and runtime
|
|
24
|
+
behavior separately. Record remaining gaps and observation dates. A mapped plan
|
|
25
|
+
or successful render is not full synchronization or accessibility certification.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oneli8-icons
|
|
3
|
+
description: Reuse or propose additions to the OneLi8 public beta neutral 24px icon pool with shared presentation and color.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Locate the public beta bundle. Read PRINCIPLES.md, packages/react/src/atoms/Icon.js,
|
|
7
|
+
packages/react/src/foundations/icons.generated.js and packages/react/src/styles/icon.css.
|
|
8
|
+
Icon owns drawing AND presentation: its `size` prop plus that stylesheet set the
|
|
9
|
+
frame, so there is no separate IconFrame module and no Icon.d.ts — types live in
|
|
10
|
+
index.d.ts and foundations/icons.generated.d.ts. These are the actual owners, not
|
|
11
|
+
templates to copy into controls.
|
|
12
|
+
|
|
13
|
+
Search OL8_ICONS first; isOl8IconName guards a name. Existing meaning uses its existing identity. For a missing
|
|
14
|
+
meaning, propose one neutral 24×24 source definition in the pool and update the
|
|
15
|
+
IconName type; no size-, color-, Gem- or consumer-specific duplicate masters.
|
|
16
|
+
Follow adjacent approved geometry and the shared semantic stroke token. Icons
|
|
17
|
+
scale GEOMETRICALLY from the 24px source, so the 1.8 stroke scales with the
|
|
18
|
+
artwork: 1.35 at 18px, 1.8 at 24px, 3.6 at 48px. Verified against Figma — the
|
|
19
|
+
Icon Frame Size=48 variant exports stroke-width 3.6 in a 48 viewBox (3.6/48 =
|
|
20
|
+
1.8/24), and the Icon Frame's own description states that no per-size source or
|
|
21
|
+
stroke tokens exist. The token set agrees: every stroke token is 1.8 and all 29
|
|
22
|
+
stroked paths in the pool are 1.8; there is no 1.2/2.4 step scale.
|
|
23
|
+
|
|
24
|
+
Do not put non-scaling-stroke on an icon. That belongs to Choice Chip
|
|
25
|
+
terminals, which stretch unevenly under preserveAspectRatio="none" and need a
|
|
26
|
+
constant edge; an icon scales uniformly and must not fight it. Filled marks
|
|
27
|
+
have no outline. Do not invent per-size stroke values.
|
|
28
|
+
|
|
29
|
+
Color follows currentColor and the consumer's semantic state. Button and
|
|
30
|
+
IconButton already own their IconFrame sizing. A new icon is a candidate until
|
|
31
|
+
Akshay approves it; an AI-generated candidate is not automatically canonical.
|
|
32
|
+
This skill is guidance, not an autonomous icon-generation program.
|
|
33
|
+
|
|
34
|
+
Test multi-part paths, clipping and color at 9, 12, 24 and a large display size;
|
|
35
|
+
test swaps in light/dark and Regular/Gem. Compare Figma geometry/instance behavior
|
|
36
|
+
when authorized access exists. Record unavailable checks instead of claiming sync.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oneli8-ui
|
|
3
|
+
description: Compose UI using the OneLi8 public beta components and tokens without recreating canonical owners.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Locate the extracted public beta bundle and read its README.md, PRINCIPLES.md and
|
|
7
|
+
packages/react/src/index.d.ts. If unavailable, obtain it rather than inventing
|
|
8
|
+
APIs. Paths here are relative to the bundle root.
|
|
9
|
+
|
|
10
|
+
Before composition, record the public component, icon identity, semantic color,
|
|
11
|
+
material, size and layout responsibility. Import from @oneli8/react; use generated
|
|
12
|
+
tokens rather than copied values. Reuse source atoms inside their existing
|
|
13
|
+
molecules. A new wrapper does not become a new atom merely because it is small.
|
|
14
|
+
|
|
15
|
+
Keep optional slots collapsible and parent/child insets intentional. Do not copy
|
|
16
|
+
private implementations into a product. For unsupported families, explicitly
|
|
17
|
+
separate consumer-owned work from canonical OneLi8; do not claim it is provided.
|
|
18
|
+
New canonical patterns need Mobbin visual research, official interaction guidance
|
|
19
|
+
and maintainer approval, not copied visual identity.
|
|
20
|
+
|
|
21
|
+
Gem has two faces and they imply different content. Choosing the wrong pairing
|
|
22
|
+
is the most common Gem error, in both directions: near-white ink on a stabilized
|
|
23
|
+
face over a light page is invisible, and ordinary dark ink on an environmental
|
|
24
|
+
face over daylight scenery is unreadable.
|
|
25
|
+
|
|
26
|
+
material.gem.color.quiet.environmentalFace is a surface over imagery or the real
|
|
27
|
+
world. Its content is material.gem.color.quiet.content, a stable near-white in
|
|
28
|
+
BOTH appearances, and it carries the material.gem.color.quiet.contentGlow halo at
|
|
29
|
+
material.gem.blur.contentGlow: four stacked shadows for text, three for control
|
|
30
|
+
geometry, because 1.4.11 asks 3:1 of a graphical object where 1.4.3 asks 4.5:1 of
|
|
31
|
+
text. That means every glyph AND the unselected control boundary and any icon, not
|
|
32
|
+
the label alone. Disabled takes material.gem.color.quiet.contentDisabled, one
|
|
33
|
+
whitish grey shared by text and control geometry, and no halo.
|
|
34
|
+
|
|
35
|
+
material.gem.color.quiet.stabilizedFace is the local content plane on app chrome,
|
|
36
|
+
including the filled Gem text field, Select and Combobox. Its content is ordinary
|
|
37
|
+
color.text.* and takes no halo. Selected and mixed indicators are never restyled
|
|
38
|
+
on either face; they fill with the primary action family and carry their own mark.
|
|
39
|
+
|
|
40
|
+
material.gem.color.quiet.legibilityFace is the reduced-transparency mode. It is
|
|
41
|
+
denser, never opaque, because Gem is worn and a solid surface would occlude the
|
|
42
|
+
world for the person who needs help reading it. It uses the same content as the
|
|
43
|
+
environmental face.
|
|
44
|
+
|
|
45
|
+
Never thicken a face to win contrast, and never widen the halo: blur spreads a
|
|
46
|
+
fixed alpha budget, so a wider halo is a weaker halo. docs/gem-material.md carries
|
|
47
|
+
the measured values and the Figma node each was read from.
|
|
48
|
+
|
|
49
|
+
Check light/dark, Regular/Gem, focus, loading/disabled, accessible names and long
|
|
50
|
+
content where applicable. Do not claim screen-reader testing from HTML checks.
|
|
51
|
+
Record changed owners and the Figma/code impact; unavailable evidence stays open.
|
|
52
|
+
The public beta's smoke tests verify packaging, not downstream UI correctness.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { OL8_ICONS, OL8_ICON_VIEWBOX } from '../foundations/icons.generated.js';
|
|
3
|
+
import { isOl8IconName } from '../foundations/geometry.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Icons are decorative by default. Meaning belongs to the parent control, so an
|
|
7
|
+
* unnamed icon is hidden from assistive technology rather than announced as an
|
|
8
|
+
* unlabelled graphic. Colour is never set here: every stroke and fill is
|
|
9
|
+
* currentColor and the consuming component supplies the semantic role.
|
|
10
|
+
*/
|
|
11
|
+
/** The bare <svg>, for slots that already carry the ol8-icon class. */
|
|
12
|
+
export function IconGlyph({ name }) {
|
|
13
|
+
if (!isOl8IconName(name)) throw new Error(`[ol8] unknown icon "${name}".`);
|
|
14
|
+
return createElement('svg', { viewBox: OL8_ICON_VIEWBOX, fill: 'none', focusable: 'false' },
|
|
15
|
+
OL8_ICONS[name].elements.map((el, i) => createElement(el.tag, { key: i, ...el.attrs })));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function Icon({ name, size = 24, label, className = '', ...rest }) {
|
|
19
|
+
if (!isOl8IconName(name)) {
|
|
20
|
+
throw new Error(`[ol8] unknown icon "${name}". Choose from OL8_ICONS rather than inventing a name.`);
|
|
21
|
+
}
|
|
22
|
+
const semantic = typeof label === 'string' && label.length > 0;
|
|
23
|
+
return createElement(
|
|
24
|
+
'span',
|
|
25
|
+
{
|
|
26
|
+
...rest,
|
|
27
|
+
className: `ol8-icon${className ? ` ${className}` : ''}`,
|
|
28
|
+
'data-ol8-icon': name,
|
|
29
|
+
'data-ol8-icon-size': size,
|
|
30
|
+
...(semantic ? { role: 'img', 'aria-label': label } : { 'aria-hidden': 'true' }),
|
|
31
|
+
},
|
|
32
|
+
createElement(IconGlyph, { name })
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createElement, forwardRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ATOM. Figma 989:484: "Content comes from item.badge; geometry and colors are
|
|
5
|
+
* semantic token controlled." One atom serves all three navigation organisms.
|
|
6
|
+
*
|
|
7
|
+
* The count sits beside a label that already names the destination, so it is
|
|
8
|
+
* not announced twice. A product that needs it announced spells it into the
|
|
9
|
+
* item's own accessible name.
|
|
10
|
+
*/
|
|
11
|
+
export const NavigationBadge = forwardRef(function NavigationBadge({ children, className = '', ...rest }, ref) {
|
|
12
|
+
if (children === undefined || children === null || children === '') {
|
|
13
|
+
throw new Error('[ol8] a navigation badge with nothing to say should not be rendered at all.');
|
|
14
|
+
}
|
|
15
|
+
return createElement('span', {
|
|
16
|
+
...rest, ref, 'aria-hidden': 'true',
|
|
17
|
+
className: `ol8-nav-badge${className ? ` ${className}` : ''}`,
|
|
18
|
+
}, children);
|
|
19
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { IconGlyph } from './Icon.js';
|
|
3
|
+
import { OL8_SELECTION_KINDS, OL8_SELECTION_STATES } from '../foundations/geometry.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Only the checked checkbox carries artwork. Mixed and selected are drawn by
|
|
7
|
+
* CSS from the token geometry, and an unset control has no mark at all.
|
|
8
|
+
*/
|
|
9
|
+
function Mark({ kind, state, icon }) {
|
|
10
|
+
if (kind === 'checkbox') {
|
|
11
|
+
if (state === 'checked') return createElement('span', { className: 'ol8-selection__mark' }, createElement(IconGlyph, { name: 'check' }));
|
|
12
|
+
if (state === 'mixed') return createElement('span', { className: 'ol8-selection__mark' });
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (kind === 'radio') return state === 'selected' ? createElement('span', { className: 'ol8-selection__mark' }) : null;
|
|
16
|
+
return createElement('span', { className: 'ol8-selection__mark' },
|
|
17
|
+
icon ? createElement('span', { className: 'ol8-icon' }, createElement(IconGlyph, { name: icon })) : null);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* ATOM. Purely presentational: it draws the mark a checkbox, radio or switch
|
|
22
|
+
* shows and owns no state, role or focus. The native input inside Choice Item
|
|
23
|
+
* owns all of that.
|
|
24
|
+
*/
|
|
25
|
+
export function SelectionIndicator({ kind, selection, size = 'compact', disabled = false, icon, className = '', ...rest }) {
|
|
26
|
+
if (!OL8_SELECTION_KINDS.includes(kind)) throw new Error(`[ol8] unknown selection kind "${kind}"`);
|
|
27
|
+
const states = OL8_SELECTION_STATES[kind];
|
|
28
|
+
const state = selection ?? states[0];
|
|
29
|
+
if (!states.includes(state)) throw new Error(`[ol8] "${state}" is not a ${kind} selection state`);
|
|
30
|
+
if (icon && kind !== 'switch') throw new Error('[ol8] only the switch thumb carries optional artwork');
|
|
31
|
+
return createElement(
|
|
32
|
+
'span',
|
|
33
|
+
{
|
|
34
|
+
...rest,
|
|
35
|
+
className: `ol8-selection ol8-selection--${kind}${className ? ` ${className}` : ''}`,
|
|
36
|
+
'data-ol8-size': size,
|
|
37
|
+
'data-ol8-selection': state,
|
|
38
|
+
'data-ol8-availability': disabled ? 'disabled' : 'enabled',
|
|
39
|
+
'aria-hidden': 'true',
|
|
40
|
+
},
|
|
41
|
+
createElement(Mark, { kind, state, icon })
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework free rules shared by every component. These are the parts of the
|
|
3
|
+
* system that are decisions rather than markup: which sizes exist, which
|
|
4
|
+
* combinations Figma approves, and which icon size a control carries.
|
|
5
|
+
*/
|
|
6
|
+
import { OL8_ICONS } from './icons.generated.js';
|
|
7
|
+
|
|
8
|
+
/** Guard against an invented glyph name reaching a component. */
|
|
9
|
+
export const isOl8IconName = (name) => Object.hasOwn(OL8_ICONS, name);
|
|
10
|
+
|
|
11
|
+
export const OL8_BUTTON_VARIANTS = ['primary', 'secondary', 'quiet', 'destructive'];
|
|
12
|
+
export const OL8_BUTTON_SIZES = ['compact', 'standard', 'comfortable', 'large'];
|
|
13
|
+
export const OL8_MATERIALS = ['regular', 'gem'];
|
|
14
|
+
/** Gem is approved for Primary and Secondary only (Figma 100:3). */
|
|
15
|
+
export const GEM_VARIANTS = ['primary', 'secondary'];
|
|
16
|
+
|
|
17
|
+
export const OL8_ICON_BUTTON_VARIANTS = OL8_BUTTON_VARIANTS;
|
|
18
|
+
export const OL8_ICON_BUTTON_SIZES = OL8_BUTTON_SIZES;
|
|
19
|
+
export const OL8_ICON_BUTTON_SHAPES = ['rounded', 'circle'];
|
|
20
|
+
|
|
21
|
+
export const OL8_SELECTION_KINDS = ['checkbox', 'radio', 'switch'];
|
|
22
|
+
export const OL8_SELECTION_SIZES = ['compact', 'comfortable'];
|
|
23
|
+
export const OL8_SELECTION_STATES = {
|
|
24
|
+
checkbox: ['unchecked', 'checked', 'mixed'],
|
|
25
|
+
radio: ['unselected', 'selected'],
|
|
26
|
+
switch: ['off', 'on'],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const OL8_CHOICE_KINDS = OL8_SELECTION_KINDS;
|
|
30
|
+
export const OL8_CHOICE_SIZES = OL8_BUTTON_SIZES;
|
|
31
|
+
export const OL8_LINK_FORMS = ['inline', 'standalone', 'navigation'];
|
|
32
|
+
export const OL8_LINK_SIZES = ['small', 'standard', 'large'];
|
|
33
|
+
export const OL8_LINK_MOTIONS = ['system', 'none'];
|
|
34
|
+
|
|
35
|
+
export const OL8_TEXT_FIELD_SIZES = OL8_BUTTON_SIZES;
|
|
36
|
+
export const OL8_TEXT_FIELD_APPEARANCES = ['outline', 'filled'];
|
|
37
|
+
export const OL8_TEXT_FIELD_MATERIALS = OL8_MATERIALS;
|
|
38
|
+
/** The four tones a message can carry. Pending is not one of them. */
|
|
39
|
+
export const OL8_MESSAGE_TONES = ['critical', 'caution', 'positive', 'informative'];
|
|
40
|
+
/** "Pending is a validation process status, not a fifth message tone." */
|
|
41
|
+
export const OL8_VALIDATION_STATUSES = ['idle', 'pending', 'resolved'];
|
|
42
|
+
/** Soft lets a person overshoot and then correct. Hard is for real technical limits. */
|
|
43
|
+
export const OL8_CHARACTER_LIMIT_BEHAVIORS = ['soft', 'hard'];
|
|
44
|
+
|
|
45
|
+
/** Form Message itself draws all five, because Figma 177:13 enumerates five. */
|
|
46
|
+
export const OL8_FORM_MESSAGE_TONES = [...OL8_MESSAGE_TONES, 'pending'];
|
|
47
|
+
/** Figma pairs one status icon with each tone (Form Message 177:13). */
|
|
48
|
+
export const OL8_FORM_MESSAGE_ICONS = {
|
|
49
|
+
critical: 'critical',
|
|
50
|
+
caution: 'caution',
|
|
51
|
+
positive: 'positive',
|
|
52
|
+
informative: 'informative',
|
|
53
|
+
pending: 'loading',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const OL8_NAVIGATION_SIZES = OL8_BUTTON_SIZES;
|
|
57
|
+
export const OL8_TABS_HIERARCHIES = ['primary', 'secondary', 'tertiary'];
|
|
58
|
+
export const OL8_TABS_ORIENTATIONS = ['horizontal', 'vertical'];
|
|
59
|
+
export const OL8_TABS_ACTIVATIONS = ['automatic', 'manual'];
|
|
60
|
+
/** "Declare exactly one behavior." */
|
|
61
|
+
export const OL8_SEGMENT_BEHAVIORS = ['single', 'multi', 'momentary'];
|
|
62
|
+
/** "Presentation never changes behavior." */
|
|
63
|
+
export const OL8_SEGMENT_PRESENTATIONS = [
|
|
64
|
+
'inset-fill', 'line-indicator', 'outlined-selection', 'soft-pill', 'icon-only', 'stacked-label',
|
|
65
|
+
];
|
|
66
|
+
export const OL8_TAB_BAR_PRESENTATIONS = ['bottom', 'inline', 'sidebar', 'spatial-rail'];
|
|
67
|
+
|
|
68
|
+
export const OL8_CHIP_SIZES = OL8_BUTTON_SIZES;
|
|
69
|
+
export const OL8_TOKEN_SIZES = OL8_CHIP_SIZES;
|
|
70
|
+
/** Figma draws the last three; `auto` resolves to one of them in code. */
|
|
71
|
+
export const OL8_TOKEN_MARKS = ['auto', 'none', 'check', 'remove'];
|
|
72
|
+
|
|
73
|
+
/** The contract's resolution, stated once so both packages agree. */
|
|
74
|
+
export function resolveTokenMark(mark, { removable = true, selected = false } = {}) {
|
|
75
|
+
if (mark !== 'auto') return mark;
|
|
76
|
+
if (removable) return 'remove';
|
|
77
|
+
return selected ? 'check' : 'none';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const OL8_OPTION_SIZES = ['standard', 'large'];
|
|
81
|
+
export const OL8_POPUP_WIDTHS = ['content', 'anchor', 'wide'];
|
|
82
|
+
export const OL8_POPUP_SIZES = OL8_OPTION_SIZES;
|
|
83
|
+
export const OL8_MULTI_SELECT_SIZES = OL8_BUTTON_SIZES;
|
|
84
|
+
export const OL8_MULTI_SELECT_APPEARANCES = ['outline', 'filled'];
|
|
85
|
+
export const OL8_MULTI_SELECT_MATERIALS = OL8_MATERIALS;
|
|
86
|
+
/** Constrained resolves from a known set; suggestive may also author values. */
|
|
87
|
+
export const OL8_VALUE_POLICIES = ['constrained', 'suggestive'];
|
|
88
|
+
|
|
89
|
+
export const OL8_COMBOBOX_SIZES = OL8_BUTTON_SIZES;
|
|
90
|
+
export const OL8_COMBOBOX_APPEARANCES = ['outline', 'filled'];
|
|
91
|
+
export const OL8_COMBOBOX_MATERIALS = OL8_MATERIALS;
|
|
92
|
+
export const OL8_AUTOCOMPLETE_MODES = ['none', 'list-manual', 'list-automatic'];
|
|
93
|
+
export const OL8_FILTER_SOURCES = ['local', 'remote', 'external'];
|
|
94
|
+
|
|
95
|
+
export const OL8_PICKER_SIZES = OL8_BUTTON_SIZES;
|
|
96
|
+
export const OL8_PICKER_MATERIALS = OL8_MATERIALS;
|
|
97
|
+
export const OL8_COMMIT_BEHAVIORS = ['immediate', 'apply'];
|
|
98
|
+
export const OL8_PICKER_PRESENTATIONS = ['inline', 'popup'];
|
|
99
|
+
/** One treatment across an instance; peers never mix Check and None. */
|
|
100
|
+
export const OL8_PICKER_MARKS = ['check', 'none'];
|
|
101
|
+
|
|
102
|
+
export const OL8_POPUP_STATUSES = ['none', 'loading', 'empty', 'error'];
|
|
103
|
+
export const OL8_AGGREGATE = ['none', 'some', 'all'];
|
|
104
|
+
|
|
105
|
+
/** Compact and Standard take the 18px icon; Comfortable and Large take 24px. */
|
|
106
|
+
export const iconSizeForButton = (size) =>
|
|
107
|
+
size === 'comfortable' || size === 'large' ? 24 : 18;
|
|
108
|
+
|
|
109
|
+
/** Artwork sizes as BUILT in Figma: 18 / 18 / 24 / 24. */
|
|
110
|
+
export const artworkSizeForIconButton = iconSizeForButton;
|
|
111
|
+
|
|
112
|
+
/** The molecule's four sizes map onto the atom's two. */
|
|
113
|
+
export const indicatorSizeFor = (size) =>
|
|
114
|
+
size === 'comfortable' || size === 'large' ? 'comfortable' : 'compact';
|
|
115
|
+
|
|
116
|
+
/** Figma's Selection axis, derived from native input state. */
|
|
117
|
+
export function selectionStateFor(kind, { checked = false, mixed = false } = {}) {
|
|
118
|
+
if (kind === 'checkbox') return mixed ? 'mixed' : checked ? 'checked' : 'unchecked';
|
|
119
|
+
if (kind === 'radio') return checked ? 'selected' : 'unselected';
|
|
120
|
+
return checked ? 'on' : 'off';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Disabled children are excluded from the tally. */
|
|
124
|
+
export function computeAggregate(children) {
|
|
125
|
+
const boxes = [...children].filter((c) => !c.disabled);
|
|
126
|
+
if (boxes.length === 0) return 'none';
|
|
127
|
+
const checked = boxes.filter((c) => c.checked).length;
|
|
128
|
+
if (checked === 0) return 'none';
|
|
129
|
+
return checked === boxes.length ? 'all' : 'some';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function assertVariant(variant, size, material) {
|
|
133
|
+
if (!OL8_BUTTON_VARIANTS.includes(variant)) throw new Error(`[ol8] unknown button variant "${variant}"`);
|
|
134
|
+
if (!OL8_BUTTON_SIZES.includes(size)) throw new Error(`[ol8] unknown button size "${size}"`);
|
|
135
|
+
if (!OL8_MATERIALS.includes(material)) throw new Error(`[ol8] unknown material "${material}"`);
|
|
136
|
+
if (material === 'gem' && !GEM_VARIANTS.includes(variant)) {
|
|
137
|
+
throw new Error(`[ol8] Gem has no approved treatment for "${variant}" — Figma 100:3 says Quiet and Destructive use the regular material.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let seq = 0;
|
|
142
|
+
/** Stable within a render pass; React 18+ callers should pass their own useId. */
|
|
143
|
+
export const autoId = (prefix) => `${prefix}-${(seq++).toString(36)}${Math.random().toString(36).slice(2, 7)}`;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// GENERATED by packages/sync/src/build-icons.mjs — do not edit by hand.
|
|
2
|
+
// Source: Figma "Oneli8 Design System — 1.0.0-beta.1", page Iconography,
|
|
3
|
+
// frame "Icon Library / Masters" (426:55). Re-run `npm run sync:icons`.
|
|
4
|
+
|
|
5
|
+
export interface Ol8IconEntry {
|
|
6
|
+
/** Figma category segment: "Icon / <Category> / <Name>". */
|
|
7
|
+
readonly category: 'action' | 'navigation' | 'status' | 'selection'
|
|
8
|
+
| 'transport' | 'vitals' | 'maneuver';
|
|
9
|
+
/** Node id of the 24px master in Figma, or null while it is a candidate. */
|
|
10
|
+
readonly figmaNode: string | null;
|
|
11
|
+
/** True until the master is approved and mapped to a Figma node. */
|
|
12
|
+
readonly candidate?: boolean;
|
|
13
|
+
/** Semantic registry controls RTL mirroring (per Iconography documentation). */
|
|
14
|
+
readonly mirrorInRTL: boolean;
|
|
15
|
+
/** The ink Figma baked into the master, discarded in favour of currentColor. */
|
|
16
|
+
readonly sourceInk: string | null;
|
|
17
|
+
/** Drawing instructions in the canonical 24-unit source space. */
|
|
18
|
+
readonly body: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export declare const OL8_ICON_VIEWBOX: '0 0 24 24';
|
|
22
|
+
export declare const OL8_ICONS: {
|
|
23
|
+
readonly "add": Ol8IconEntry;
|
|
24
|
+
readonly "caution": Ol8IconEntry;
|
|
25
|
+
readonly "check": Ol8IconEntry;
|
|
26
|
+
readonly "chevron-down": Ol8IconEntry;
|
|
27
|
+
readonly "chevron-up": Ol8IconEntry;
|
|
28
|
+
readonly "clear": Ol8IconEntry;
|
|
29
|
+
readonly "close": Ol8IconEntry;
|
|
30
|
+
readonly "copy": Ol8IconEntry;
|
|
31
|
+
readonly "critical": Ol8IconEntry;
|
|
32
|
+
readonly "forward": Ol8IconEntry;
|
|
33
|
+
readonly "heart": Ol8IconEntry;
|
|
34
|
+
readonly "informative": Ol8IconEntry;
|
|
35
|
+
readonly "loading": Ol8IconEntry;
|
|
36
|
+
readonly "maneuver-right": Ol8IconEntry;
|
|
37
|
+
readonly "mixed": Ol8IconEntry;
|
|
38
|
+
readonly "pause": Ol8IconEntry;
|
|
39
|
+
readonly "play": Ol8IconEntry;
|
|
40
|
+
readonly "positive": Ol8IconEntry;
|
|
41
|
+
readonly "search": Ol8IconEntry;
|
|
42
|
+
readonly "selected-dot": Ol8IconEntry;
|
|
43
|
+
readonly "skip-back": Ol8IconEntry;
|
|
44
|
+
readonly "skip-forward": Ol8IconEntry;
|
|
45
|
+
readonly "visibility-closed": Ol8IconEntry;
|
|
46
|
+
readonly "visibility-open": Ol8IconEntry;
|
|
47
|
+
};
|
|
48
|
+
export type Ol8IconName = keyof typeof OL8_ICONS;
|