@inkra/react 0.0.0-stage → 0.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.
Files changed (267) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +316 -2
  3. package/THIRD_PARTY_NOTICES.md +39 -0
  4. package/Yozai-OFL.txt +94 -0
  5. package/dist/Button.d.ts +36 -0
  6. package/dist/Button.js +89 -0
  7. package/dist/PencilIcon.d.ts +22 -0
  8. package/dist/PencilIcon.js +210 -0
  9. package/dist/PencilLine.d.ts +10 -0
  10. package/dist/PencilLine.js +91 -0
  11. package/dist/PencilSurface.d.ts +19 -0
  12. package/dist/PencilSurface.js +19 -0
  13. package/dist/PencilText.d.ts +16 -0
  14. package/dist/PencilText.js +338 -0
  15. package/dist/accordion/Accordion.d.ts +27 -0
  16. package/dist/accordion/Accordion.js +40 -0
  17. package/dist/accordion/index.d.ts +2 -0
  18. package/dist/accordion/index.js +1 -0
  19. package/dist/alert-dialog/index.d.ts +64 -0
  20. package/dist/alert-dialog/index.js +109 -0
  21. package/dist/assets/ui-hand-yozai.woff2 +0 -0
  22. package/dist/audio-player/index.d.ts +60 -0
  23. package/dist/audio-player/index.js +101 -0
  24. package/dist/breadcrumb/index.d.ts +16 -0
  25. package/dist/breadcrumb/index.js +25 -0
  26. package/dist/calendar/index.d.ts +21 -0
  27. package/dist/calendar/index.js +41 -0
  28. package/dist/carousel/index.d.ts +51 -0
  29. package/dist/carousel/index.js +290 -0
  30. package/dist/choice-grid/index.d.ts +38 -0
  31. package/dist/choice-grid/index.js +85 -0
  32. package/dist/color-picker/color.d.ts +15 -0
  33. package/dist/color-picker/color.js +55 -0
  34. package/dist/color-picker/index.d.ts +31 -0
  35. package/dist/color-picker/index.js +69 -0
  36. package/dist/command/index.d.ts +23 -0
  37. package/dist/command/index.js +39 -0
  38. package/dist/content/index.d.ts +106 -0
  39. package/dist/content/index.js +80 -0
  40. package/dist/context-menu/index.d.ts +76 -0
  41. package/dist/context-menu/index.js +240 -0
  42. package/dist/data-table/index.d.ts +47 -0
  43. package/dist/data-table/index.js +91 -0
  44. package/dist/date-picker/index.d.ts +50 -0
  45. package/dist/date-picker/index.js +180 -0
  46. package/dist/day-agenda/index.d.ts +43 -0
  47. package/dist/day-agenda/index.js +47 -0
  48. package/dist/day-agenda/layout.d.ts +20 -0
  49. package/dist/day-agenda/layout.js +60 -0
  50. package/dist/display/ProgressRing.d.ts +6 -0
  51. package/dist/display/ProgressRing.js +146 -0
  52. package/dist/display/index.d.ts +59 -0
  53. package/dist/display/index.js +75 -0
  54. package/dist/dockable-panel/index.d.ts +41 -0
  55. package/dist/dockable-panel/index.js +253 -0
  56. package/dist/dockable-panel/model.d.ts +21 -0
  57. package/dist/dockable-panel/model.js +61 -0
  58. package/dist/drawer/index.d.ts +63 -0
  59. package/dist/drawer/index.js +86 -0
  60. package/dist/dropdown-menu/index.d.ts +34 -0
  61. package/dist/dropdown-menu/index.js +40 -0
  62. package/dist/editable/index.d.ts +53 -0
  63. package/dist/editable/index.js +162 -0
  64. package/dist/empty/index.d.ts +21 -0
  65. package/dist/empty/index.js +23 -0
  66. package/dist/flip/index.d.ts +49 -0
  67. package/dist/flip/index.js +138 -0
  68. package/dist/form/EditablePencilText.d.ts +13 -0
  69. package/dist/form/EditablePencilText.js +323 -0
  70. package/dist/form/Field.d.ts +13 -0
  71. package/dist/form/Field.js +26 -0
  72. package/dist/form/Input.d.ts +27 -0
  73. package/dist/form/Input.js +97 -0
  74. package/dist/form/InputAction.d.ts +7 -0
  75. package/dist/form/InputAction.js +24 -0
  76. package/dist/form/Textarea.d.ts +17 -0
  77. package/dist/form/Textarea.js +102 -0
  78. package/dist/form/context.d.ts +12 -0
  79. package/dist/form/context.js +10 -0
  80. package/dist/form/controlProps.d.ts +23 -0
  81. package/dist/form/controlProps.js +20 -0
  82. package/dist/form/index.d.ts +7 -0
  83. package/dist/form/index.js +3 -0
  84. package/dist/form/types.d.ts +6 -0
  85. package/dist/form/types.js +1 -0
  86. package/dist/heatmap/index.d.ts +59 -0
  87. package/dist/heatmap/index.js +248 -0
  88. package/dist/hotkey/index.d.ts +37 -0
  89. package/dist/hotkey/index.js +130 -0
  90. package/dist/hotkey/model.d.ts +27 -0
  91. package/dist/hotkey/model.js +97 -0
  92. package/dist/hover-card/index.d.ts +33 -0
  93. package/dist/hover-card/index.js +141 -0
  94. package/dist/image-cropper/index.d.ts +82 -0
  95. package/dist/image-cropper/index.js +261 -0
  96. package/dist/image-cropper/model.d.ts +42 -0
  97. package/dist/image-cropper/model.js +94 -0
  98. package/dist/image-viewer/index.d.ts +79 -0
  99. package/dist/image-viewer/index.js +220 -0
  100. package/dist/image-viewer/model.d.ts +9 -0
  101. package/dist/image-viewer/model.js +18 -0
  102. package/dist/images/layers-2x.png +0 -0
  103. package/dist/images/layers.png +0 -0
  104. package/dist/images/marker-icon.png +0 -0
  105. package/dist/index.d.ts +75 -0
  106. package/dist/index.js +69 -0
  107. package/dist/input-number/index.d.ts +16 -0
  108. package/dist/input-number/index.js +77 -0
  109. package/dist/internal/InputActionContext.d.ts +1 -0
  110. package/dist/internal/InputActionContext.js +3 -0
  111. package/dist/internal/cache.d.ts +14 -0
  112. package/dist/internal/cache.js +54 -0
  113. package/dist/internal/line.d.ts +4 -0
  114. package/dist/internal/line.js +21 -0
  115. package/dist/internal/paper.d.ts +1 -0
  116. package/dist/internal/paper.js +65 -0
  117. package/dist/internal/random.d.ts +3 -0
  118. package/dist/internal/random.js +30 -0
  119. package/dist/internal/recovery.d.ts +1 -0
  120. package/dist/internal/recovery.js +52 -0
  121. package/dist/internal/renderTokens.d.ts +10 -0
  122. package/dist/internal/renderTokens.js +13 -0
  123. package/dist/internal/stroke.d.ts +24 -0
  124. package/dist/internal/stroke.js +144 -0
  125. package/dist/internal/surfaceRenderer.d.ts +41 -0
  126. package/dist/internal/surfaceRenderer.js +178 -0
  127. package/dist/internal/textQueue.d.ts +1 -0
  128. package/dist/internal/textQueue.js +37 -0
  129. package/dist/internal/textRenderer.d.ts +28 -0
  130. package/dist/internal/textRenderer.js +239 -0
  131. package/dist/internal/travelIcons.d.ts +24 -0
  132. package/dist/internal/travelIcons.js +14 -0
  133. package/dist/internal/useBrowserLayoutEffect.d.ts +2 -0
  134. package/dist/internal/useBrowserLayoutEffect.js +3 -0
  135. package/dist/internal/usePencilSurface.d.ts +3 -0
  136. package/dist/internal/usePencilSurface.js +116 -0
  137. package/dist/internal/useSelectionDrawing.d.ts +2 -0
  138. package/dist/internal/useSelectionDrawing.js +90 -0
  139. package/dist/internal/wash.d.ts +16 -0
  140. package/dist/internal/wash.js +149 -0
  141. package/dist/journey-map/MapScale.d.ts +4 -0
  142. package/dist/journey-map/MapScale.js +54 -0
  143. package/dist/journey-map/index.d.ts +39 -0
  144. package/dist/journey-map/index.js +197 -0
  145. package/dist/journey-map/terrain.d.ts +8 -0
  146. package/dist/journey-map/terrain.js +127 -0
  147. package/dist/keypad/index.d.ts +23 -0
  148. package/dist/keypad/index.js +63 -0
  149. package/dist/keypad/model.d.ts +20 -0
  150. package/dist/keypad/model.js +33 -0
  151. package/dist/layout/index.d.ts +63 -0
  152. package/dist/layout/index.js +62 -0
  153. package/dist/log/index.d.ts +45 -0
  154. package/dist/log/index.js +159 -0
  155. package/dist/log/model.d.ts +16 -0
  156. package/dist/log/model.js +13 -0
  157. package/dist/node-graph/index.d.ts +123 -0
  158. package/dist/node-graph/index.js +304 -0
  159. package/dist/number-ticker/index.d.ts +29 -0
  160. package/dist/number-ticker/index.js +132 -0
  161. package/dist/number-ticker/model.d.ts +8 -0
  162. package/dist/number-ticker/model.js +34 -0
  163. package/dist/overflow-list/index.d.ts +39 -0
  164. package/dist/overflow-list/index.js +230 -0
  165. package/dist/overflow-list/layout.d.ts +19 -0
  166. package/dist/overflow-list/layout.js +44 -0
  167. package/dist/overlay/Overlay.d.ts +65 -0
  168. package/dist/overlay/Overlay.js +77 -0
  169. package/dist/overlay/Popover.d.ts +43 -0
  170. package/dist/overlay/Popover.js +45 -0
  171. package/dist/overlay/index.d.ts +4 -0
  172. package/dist/overlay/index.js +2 -0
  173. package/dist/overlay/themeBridge.d.ts +8 -0
  174. package/dist/overlay/themeBridge.js +98 -0
  175. package/dist/pagination/index.d.ts +23 -0
  176. package/dist/pagination/index.js +41 -0
  177. package/dist/pagination/model.d.ts +4 -0
  178. package/dist/pagination/model.js +42 -0
  179. package/dist/pan-zoom/index.d.ts +71 -0
  180. package/dist/pan-zoom/index.js +175 -0
  181. package/dist/pencil.d.ts +10 -0
  182. package/dist/pencil.js +39 -0
  183. package/dist/radar/index.d.ts +54 -0
  184. package/dist/radar/index.js +220 -0
  185. package/dist/range-slider/index.d.ts +16 -0
  186. package/dist/range-slider/index.js +22 -0
  187. package/dist/rating/index.d.ts +28 -0
  188. package/dist/rating/index.js +127 -0
  189. package/dist/resizable/index.d.ts +32 -0
  190. package/dist/resizable/index.js +50 -0
  191. package/dist/scroll-area/index.d.ts +36 -0
  192. package/dist/scroll-area/index.js +44 -0
  193. package/dist/segmented-progress/SegmentedProgress.d.ts +17 -0
  194. package/dist/segmented-progress/SegmentedProgress.js +42 -0
  195. package/dist/select/MultipleSelect.d.ts +44 -0
  196. package/dist/select/MultipleSelect.js +124 -0
  197. package/dist/select/SearchableSelect.d.ts +45 -0
  198. package/dist/select/SearchableSelect.js +50 -0
  199. package/dist/select/Select.d.ts +109 -0
  200. package/dist/select/Select.js +205 -0
  201. package/dist/select/index.d.ts +4 -0
  202. package/dist/select/index.js +2 -0
  203. package/dist/selection/Checkbox.d.ts +9 -0
  204. package/dist/selection/Checkbox.js +37 -0
  205. package/dist/selection/RadioGroup.d.ts +23 -0
  206. package/dist/selection/RadioGroup.js +82 -0
  207. package/dist/selection/Switch.d.ts +8 -0
  208. package/dist/selection/Switch.js +36 -0
  209. package/dist/selection/index.d.ts +6 -0
  210. package/dist/selection/index.js +3 -0
  211. package/dist/selection/useNativeChecked.d.ts +13 -0
  212. package/dist/selection/useNativeChecked.js +27 -0
  213. package/dist/slider/index.d.ts +11 -0
  214. package/dist/slider/index.js +45 -0
  215. package/dist/slot/index.d.ts +24 -0
  216. package/dist/slot/index.js +29 -0
  217. package/dist/sortable/index.d.ts +50 -0
  218. package/dist/sortable/index.js +278 -0
  219. package/dist/sortable/model.d.ts +30 -0
  220. package/dist/sortable/model.js +64 -0
  221. package/dist/sparkline/index.d.ts +19 -0
  222. package/dist/sparkline/index.js +240 -0
  223. package/dist/sparkline/model.d.ts +16 -0
  224. package/dist/sparkline/model.js +28 -0
  225. package/dist/steps/index.d.ts +16 -0
  226. package/dist/steps/index.js +37 -0
  227. package/dist/styles.css +8226 -0
  228. package/dist/tabs/index.d.ts +22 -0
  229. package/dist/tabs/index.js +42 -0
  230. package/dist/tag-input/index.d.ts +69 -0
  231. package/dist/tag-input/index.js +325 -0
  232. package/dist/tag-input/model.d.ts +14 -0
  233. package/dist/tag-input/model.js +38 -0
  234. package/dist/time-picker/index.d.ts +15 -0
  235. package/dist/time-picker/index.js +168 -0
  236. package/dist/timeline/index.d.ts +14 -0
  237. package/dist/timeline/index.js +16 -0
  238. package/dist/toast/index.d.ts +27 -0
  239. package/dist/toast/index.js +36 -0
  240. package/dist/toggle-group/index.d.ts +17 -0
  241. package/dist/toggle-group/index.js +26 -0
  242. package/dist/tokens.css +356 -0
  243. package/dist/tooltip/Tooltip.d.ts +24 -0
  244. package/dist/tooltip/Tooltip.js +32 -0
  245. package/dist/tooltip/index.d.ts +2 -0
  246. package/dist/tooltip/index.js +1 -0
  247. package/dist/transfer/index.d.ts +64 -0
  248. package/dist/transfer/index.js +174 -0
  249. package/dist/transfer/model.d.ts +12 -0
  250. package/dist/transfer/model.js +33 -0
  251. package/dist/tree/index.d.ts +39 -0
  252. package/dist/tree/index.js +36 -0
  253. package/dist/tree-select/index.d.ts +59 -0
  254. package/dist/tree-select/index.js +142 -0
  255. package/dist/tree-select/model.d.ts +31 -0
  256. package/dist/tree-select/model.js +135 -0
  257. package/dist/typewriter/index.d.ts +21 -0
  258. package/dist/typewriter/index.js +101 -0
  259. package/dist/typewriter/model.d.ts +7 -0
  260. package/dist/typewriter/model.js +45 -0
  261. package/dist/upload/index.d.ts +59 -0
  262. package/dist/upload/index.js +230 -0
  263. package/dist/virtual-list/index.d.ts +44 -0
  264. package/dist/virtual-list/index.js +128 -0
  265. package/dist/virtual-list/model.d.ts +7 -0
  266. package/dist/virtual-list/model.js +27 -0
  267. package/package.json +119 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Inkra contributors
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 CHANGED
@@ -1,3 +1,317 @@
1
- # Temporary Holding Version
1
+ # Inkra
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Inkra is a pencil-styled React component library. It includes action, form, overlay, navigation, content and feedback components, plus Calendar, DayAgenda, JourneyMap and deterministic drawing primitives. Version `0.1.0` is published; the component expansion described below is available only in the current, unpublished source candidate.
4
+
5
+ Source and maintenance: [inkrajs/inkra](https://github.com/inkrajs/inkra).
6
+
7
+ Package name: `@inkra/react`. Import the scoped package and its stylesheet;
8
+ the unscoped `inkra` name is not this library's published package.
9
+
10
+ ## Installation
11
+
12
+ The package is prepared for npm-compatible installation, but this repository does not assume that a release has been published. In a workspace, depend on the local package according to the workspace package manager's conventions.
13
+
14
+ The manifest declares React 18 or newer as a peer dependency; release checks target React 18 and 19. ESM, TypeScript declarations, CSS and font assets are included. The release pipeline verifies SSR-to-browser hydration from the installed package, including form controls, portals, canvas recovery and the lazy-loaded map. Do not interpret the peer range as testing every future React version.
15
+
16
+ Inkra components require a React client boundary. Next.js and React Server Components are not a certified integration in 0.1.0; do not import interactive components directly into a Server Component. React 18 development SSR of Calendar still reports a layout-effect warning from react-day-picker 9; its browser hydration is checked separately. Applications requiring warning-free React 18 SSR should mount Calendar after hydration. Server and client must use matching locale, dates, values and theme inputs.
17
+
18
+ ## Usage
19
+
20
+ Import the component styles once near the application entry point:
21
+
22
+ ```tsx
23
+ import '@inkra/react/styles.css';
24
+
25
+ import { Button, ButtonGroup, ButtonLink, PencilLine, PencilSurface, PencilText } from '@inkra/react';
26
+
27
+ export function Actions() {
28
+ return (
29
+ <>
30
+ <ButtonGroup aria-label="Document actions">
31
+ <Button variant="primary" size="md" seed="save-action">
32
+ Save changes
33
+ </Button>
34
+ <ButtonLink href="/preview" variant="outline">
35
+ Preview
36
+ </ButtonLink>
37
+ </ButtonGroup>
38
+ <div>
39
+ <PencilLine seed="section-divider" />
40
+ <PencilSurface variant="paper" seed="note-paper">
41
+ <PencilText seed="note-title">Project notes</PencilText>
42
+ </PencilSurface>
43
+ </div>
44
+ </>
45
+ );
46
+ }
47
+ ```
48
+
49
+ To use only the design tokens, import `@inkra/react/tokens.css` instead.
50
+
51
+ ## Button
52
+
53
+ `Button` forwards its ref and native button attributes. Its default `type` is `button`, so placing it inside a form will not submit unexpectedly.
54
+
55
+ ```tsx
56
+ <Button variant="secondary" size="sm" onClick={handleCancel}>
57
+ Cancel
58
+ </Button>
59
+
60
+ <Button loading seed="save-request">
61
+ Save
62
+ </Button>
63
+ ```
64
+
65
+ Supported Inkra props:
66
+
67
+ - `variant`: `primary`, `secondary`, `outline`, `ghost`, `destructive`, or `link`
68
+ - `size`: `xs`, `sm`, `md`, `lg`, `icon-xs`, `icon-sm`, `icon`, or `icon-lg`
69
+ - `loading`: marks the button busy and disables interaction
70
+ - `seed`: controls the deterministic hand-drawn decoration
71
+ - `startIcon` / `endIcon`: preserve logical icon placement in LTR and RTL layouts
72
+
73
+ All standard `button` props, including `disabled`, `name`, `value`, `form`, `aria-*`, and `data-*`, are passed to the native element.
74
+
75
+ `ButtonLink` renders a native anchor with the same visual props. Disabled or loading links remove their `href`, leave the tab order, expose `aria-disabled`, and block primary, modified, auxiliary and keyboard navigation. `ButtonGroup` renders `role="group"` with horizontal or vertical layout; provide `aria-label` or `aria-labelledby`. Icon-only controls use a square visual size and require an accessible name.
76
+
77
+ ## Form Controls and Select
78
+
79
+ `Input` defaults to an underline appearance, with explicit `outline`, `soft` and `ghost` variants. It supports `startIcon`, `endIcon`, `prefix`, `suffix`, `clearable`, `loading`, `startAction`, `endAction`, `fullWidth` and logical `textAlign`. Use `onValueChange` for edits and clearing; native `onChange` does not receive fabricated clear events. `Field` connects labels, descriptions and errors.
80
+
81
+ `InputNumber` places decrease and increase buttons on either side of a centered numeric input, with no clear button. Keyboard deletion produces `null`; decimal stepping and out-of-range validation remain native. `Slider` supports single/range values, horizontal/vertical layout and `standard` or `inset` appearance.
82
+
83
+ `Select.Root` uses string values for single selection and string arrays with `multiple`. Multi-select remains open while toggling items and submits repeated values under the same field name. `Select.Clear` is a separate button; never nest it inside the trigger.
84
+
85
+ `Select.Trigger` supports `startIcon`, `endIcon`, `hideValue`, `hideIndicator`, `iconOnly`, `fullWidth` and `textAlign`. The existing `icon` prop replaces the dropdown indicator. Icon-only mode keeps a square hit target of at least 44px and retains selected text as an accessible description. Provide a Field/ARIA name and Tooltip for icon-only triggers. Decorative slots must not contain interactive controls.
86
+
87
+ `Select.Searchable` is a separate combobox API accepting `items` with `value`, `label`, optional `disabled` and `keywords`. `searchPlacement="trigger"` uses an editable input; `"popup"` places search inside the popup. It supports single/multiple values, filtering and clear controls. The compound Trigger appearance props do not implicitly apply to this separate API.
88
+
89
+ ## Overlay Sections
90
+
91
+ `Overlay.Content layout="sections"` composes a fixed `Header`, a scrolling `Body`
92
+ and a fixed `Footer`. The default `layout="plain"` preserves the original padded
93
+ content layout. Keep the sections as direct children of Content.
94
+
95
+ ```tsx
96
+ <Overlay.Root>
97
+ <Overlay.Trigger asChild><Button>Edit note</Button></Overlay.Trigger>
98
+ <Overlay.Portal>
99
+ <Overlay.Backdrop />
100
+ <Overlay.Content layout="sections">
101
+ <Overlay.Header closeLabel="Close">
102
+ <Overlay.Title>Edit note</Overlay.Title>
103
+ <Overlay.Description>Update the note title.</Overlay.Description>
104
+ </Overlay.Header>
105
+ <Overlay.Body>{/* Fields or other dialog content */}</Overlay.Body>
106
+ <Overlay.Footer>
107
+ <Overlay.Close asChild><Button variant="outline">Cancel</Button></Overlay.Close>
108
+ </Overlay.Footer>
109
+ </Overlay.Content>
110
+ </Overlay.Portal>
111
+ </Overlay.Root>
112
+ ```
113
+
114
+ Header includes a pencil close button by default (`showClose={false}` hides it).
115
+ Localize its accessible name and tooltip with `closeLabel`; `action` adds extra
116
+ header controls. Header/Footer use pencil separators by default; disable each
117
+ with `divided={false}`. Footer `align` is `start | center | end` (default end) and
118
+ wraps actions when needed. `Overlay.CloseButton label="Close"` is also available
119
+ for custom layouts. Native attributes, handlers and refs are forwarded.
120
+
121
+ Keep a form inside Body and link Footer submit buttons with matching `form`/`id`
122
+ attributes. This preserves validation and submission without wrapping the
123
+ sections in another scroll container. Radix continues to manage Escape,
124
+ outside interaction, modal focus trapping and focus return.
125
+
126
+ ## Navigation And Feedback
127
+
128
+ `Tabs` exposes `Root`, `List`, `Trigger` and `Content`. It supports controlled or default values, horizontal/vertical orientation, RTL, automatic/manual activation and disabled triggers. `Content forceMount` preserves child state while hiding inactive panels. The selected pigment draws in and erases out, with reduced-motion fallbacks.
129
+
130
+ `Root.size` accepts `sm | md | lg` (default `md`), and `Root.animated={false}` disables pigment animation. `List.align` accepts `start | center | end`, `List.fullWidth` distributes triggers equally, and `List.loop={false}` stops keyboard wrapping. `Trigger.startIcon` and `Trigger.endIcon` accept decorative React nodes; provide an accessible name for icon-only triggers. Small tabs keep a minimum 44px hit target.
131
+
132
+ `List.divided` adds pencil separators between adjacent triggers (default false), following horizontal or vertical orientation without changing keyboard navigation.
133
+
134
+ `Tooltip` exposes `Provider`, `Root`, `Trigger`, `Portal` and `Content`. Use it for supplementary non-interactive text, never as the only accessible name or essential instructions. Portal content inherits the trigger's theme.
135
+
136
+ `Accordion` exposes `Root`, `Item`, `Header`, `Trigger` and `Content`. Use `type="single"` with a string value or `type="multiple"` with a string array. Single mode accepts `collapsible`; both support disabled items and keyboard navigation.
137
+
138
+ ```tsx
139
+ <Tabs.Root defaultValue="notes">
140
+ <Tabs.List aria-label="Workspace">
141
+ <Tabs.Trigger value="notes">Notes</Tabs.Trigger>
142
+ <Tabs.Trigger value="drafts">Drafts</Tabs.Trigger>
143
+ </Tabs.List>
144
+ <Tabs.Content value="notes">Saved notes</Tabs.Content>
145
+ <Tabs.Content value="drafts">Unpublished drafts</Tabs.Content>
146
+ </Tabs.Root>
147
+
148
+ <Badge variant="success">Saved</Badge>
149
+ <Separator decorative />
150
+ <Skeleton style={{ width: 200, height: 20 }} />
151
+ <Progress label="Uploading" value={60} />
152
+ <Progress aria-label="Loading" value={null} />
153
+ <Progress shape="circular" value={72} diameter={128} thickness={6} aria-label="Download">
154
+ {({ percent }) => `${Math.round(percent ?? 0)}%`}
155
+ </Progress>
156
+ <Progress shape="circular" value={3} max={5} contentPosition="end" label="Files">
157
+ <span>3 / 5 files</span>
158
+ </Progress>
159
+ ```
160
+
161
+ `Badge` variants are `neutral`, `accent`, `success`, `warning`, `danger` and `info`. `Separator` has horizontal/vertical orientation and a decorative opt-out. `Skeleton` is a static, aria-hidden placeholder; mark its loading container `aria-busy`. `Progress` clamps numeric values to `0..max` (default 100); `null` is indeterminate without a fabricated percentage. Always provide a progress label.
162
+
163
+ `Badge.size` accepts `sm | md | lg` with optional `startIcon` and `endIcon`. `Separator.weight` clamps to 0.3–2 (default 0.9). `Skeleton.radius` uses the existing 0–8px pencil corner contract. `Progress.size` selects a 12/16/24px track (`sm/md/lg`); `dir`, `aria-valuetext`, refs and native DOM attributes remain available. Tooltip exposes the underlying delay, alignment, collision and hoverable-content controls alongside `Content.surfaceRadius`.
164
+
165
+ Circular Progress uses a grainy pencil arc with naturally tapered ends and an
166
+ empty gap for the remaining progress.
167
+ `size` selects a 64/96/128px diameter; `diameter` overrides it (32-512px), and
168
+ `thickness` sets the base stroke width (1-diameter/8px, affected by global stroke
169
+ settings). `children` accepts non-interactive icons/text or a render function
170
+ receiving normalized `{ value, max, percent }`; unknown value/percent are null.
171
+ `contentPosition` supports `center`, `top`, `bottom`, `start`, and `end`; logical
172
+ sides follow RTL. Circular defaults to center, linear to end (including explicit
173
+ center). Keep center content short; longer descriptions belong outside the ring.
174
+ Always supply a label or ARIA name, even for icon content. Progress updates reveal
175
+ the existing texture; only unknown progress rotates, respecting reduced motion.
176
+ Canvas failures, forced colors and printing retain an SVG progress fallback.
177
+
178
+ ## Menus And Content
179
+
180
+ `DropdownMenu` exposes `Root`, `Trigger`, `Portal`, `Content`, `Item`, `CheckboxItem`, `RadioGroup`, `RadioItem`, `Group`, `Label` and `Separator`. It supports controlled open state, placement/collision props, disabled items, keyboard navigation and focus return. Use `Trigger asChild` with a Button; use `Item.destructive` for dangerous actions. Portal content inherits the trigger theme. Submenus are not included yet.
181
+
182
+ `Alert.Root` accepts `variant="info | success | warning | danger"`, an optional decorative `icon`, and native attributes. Compose with `Alert.Title` and `Alert.Description`. The default `role="status"` is polite; explicitly use `role="alert"` only for urgent feedback.
183
+
184
+ `Avatar.Root` provides fixed `sm/md/lg` sizes (32/44/64px), with `Avatar.Image` and `Avatar.Fallback`. Images require `alt`; name meaningful fallback content with `role="img"` and `aria-label`. Fallback supports `delayMs` and appears when the image cannot load.
185
+
186
+ `Card` exposes `Root`, `Header`, `Title`, `Description`, `Content` and `Footer`. Root accepts `variant="paper | quiet | outline"`, `radius` (0-8px) and `seed`; Title accepts `as="h2 | h3 | h4 | h5 | h6"` (default h3). Native props and refs remain available.
187
+
188
+ `Table` exposes `Container`, `Root`, `Header`, `Body`, `Footer`, `Row`, `Head`, `Cell` and `Caption`, preserving native table semantics. Root accepts `density="compact | comfortable"`; `Row.selected` is visual emphasis only. Give the scrollable Container an accessible name and use a Caption and appropriate Head scope. Sorting, filtering, pagination and grid selection are not built in.
189
+
190
+ Content layout options preserve existing defaults:
191
+
192
+ - `Card.Root.size` and `Alert.Root.size` accept `sm | md | lg` for 12/16/24px padding (default md), without reducing text size.
193
+ - `Card.Root.divided` defaults to true; `Header.divided` and `Footer.divided` override it locally. `Header.action` accepts an independent action node. `Content.padding="none"` removes content padding for edge-to-edge content. `Footer.justify` accepts `start | center | end | between`, respecting text direction.
194
+ - `Alert.Root.appearance` accepts `soft | outline` (default soft). `actions` places actual controls after the message; it does not add automatic dismissal or focus changes.
195
+ - `Avatar.Root.radius` clamps to 0-8px, including pencil and image corners; `bordered={false}` removes the outline. `Avatar.Image.fit` accepts `cover | contain` (default cover).
196
+ - `Table.Root.striped`, `hoverable` and `stickyHeader` default to false. Constrain `Table.Container` with `style={{ maxHeight: 240 }}` for sticky scrolling. `captionSide` accepts `top | bottom`. `Head.align` and `Cell.align` accept logical `start | center | end`, retaining native alignment values for compatibility. Hover feedback does not imply row actions.
197
+
198
+ ## Component Expansion (Unreleased)
199
+
200
+ These exports are not present in the published 0.1.0 archive. Build, pack and
201
+ install the local candidate to use them. The documentation playground includes
202
+ working examples, live property tabs and per-component API/accessibility notes.
203
+
204
+ | Component | Contract |
205
+ | --- | --- |
206
+ | Toast | `Provider`, `useToastManager`, `createToastManager`; add/update/close/promise notifications, named actions and timer pause managed by Base UI |
207
+ | AlertDialog | Always-modal confirmation; `Root`, `Trigger`, `Portal`, `Backdrop`, `Content`, `Header`, `Body`, `Footer`, `Title`, `Description`, `Cancel`, `Action`; pending blocks user dismissal, async actions use `closeOnAction={false}` |
208
+ | Drawer | Dialog parts plus section layout, four `Content.direction` values and token/custom `size`; focus trap, Escape and return focus, no swipe gesture API |
209
+ | DatePicker | Calendar/Popover composition; single/range local dates, controlled/default values, clear, form fields and localized labels; no UTC conversion |
210
+ | TimePicker | Native time input with clear, disabled/readOnly, min/max/step and optional seconds; browser/OS controls the native presentation |
211
+ | Pagination | Page count/current page, boundary actions and ellipses; buttons change local state rather than pretending to navigate URLs |
212
+ | Empty | Optional `Root`, `Icon`, `Title`, `Description`, `Actions`; no mandatory card or live-region role |
213
+ | ToggleGroup | `Root` and `Item`; array values for single/multiple toggles, roving focus and `aria-pressed`, not tabs |
214
+ | Command | cmdk-backed search, grouped commands, empty/disabled states and optional modal composition |
215
+ | Upload | File selection/drop, type/size/count rejection, removal, progress, cancel and retry; application transport receives `AbortSignal` and `reportProgress` |
216
+ | DataTable | Typed columns, required stable `getRowId`, sorting, filtering, pagination, selection, empty/loading state; native table semantics |
217
+ | Tree | `Root`, `Item`, `ItemContent`, `ItemTitle`, `ItemDescription`; stable keys, expansion and selection via React Aria; no drag/drop |
218
+ | Steps | Ordered `items`, `current`, orientation and optional `onStepChange`; status and disabled steps |
219
+ | Timeline | Ordered `items` with date/title/description/content, semantic `dateTime` and horizontal/vertical orientation |
220
+
221
+ ## Utility Components (Unreleased)
222
+
223
+ `Slot` is a fixed square visual container for arbitrary icons or content. Static
224
+ slots render a `div`; `interactive` slots render one native button with
225
+ `aria-pressed` selection and native Enter/Space behavior. `empty` preserves
226
+ geometry, `count` reuses Badge, and `renderCount` keeps custom rendering at the
227
+ logical corner. Keep nested controls out of interactive slots. Slot does not
228
+ implement equipment rules, rarity, sorting or drag and drop.
229
+
230
+ `Hotkey` formats shortcut values for Mac or Windows. `HotkeyInput` records only
231
+ while its native input is focused, supports controlled/uncontrolled values,
232
+ clear, Escape cancellation, read-only/disabled states, IME safeguards and
233
+ caller-owned conflict validation. It does not register global shortcut listeners
234
+ or suppress browser shortcuts.
235
+
236
+ `NumberTicker` formats values with `Intl.NumberFormat`, optionally animates from
237
+ the currently displayed frame, and can show a temporary signed delta. Reduced
238
+ motion and SSR show deterministic final values. Intermediate frames are visual;
239
+ the polite announcement updates only at the final value. Use `getValueLabel`
240
+ when units or custom rendering need a complete accessible phrase.
241
+
242
+ `Log` renders caller-ordered records with stable ids, optional level filters and
243
+ pause/follow controls. It follows only append updates while the viewport remains
244
+ near the bottom. The named scroll region supports Home/End and intentionally is
245
+ not a live region, so a long stream is not repeatedly announced. Log does not
246
+ provide persistence, virtualization or transport.
247
+
248
+ Upload does not send files anywhere unless the application supplies `onUpload`.
249
+ Without a transport, files remain selected, not successfully uploaded. Client
250
+ validation is a convenience; the receiving server must validate files itself.
251
+ DataTable does not replace `Table` and does not promise virtualization, cell
252
+ editing or server-side transport. Tree is not a file-system permission boundary.
253
+ All examples use public sample data.
254
+
255
+ ## Travel Components
256
+
257
+ `Calendar` wraps React DayPicker with default `appearance="itinerary"` day cards and optional `appearance="picker"`. It retains single/multiple/range selection, month control, disabled matchers, locale and keyboard navigation. Supply `selected` and `onSelect` for controlled selection. Events have independent `onEventSelect` handling; `displayRange` and `renderDayAccessory` customize the itinerary. Narrow itinerary grids retain seven columns and hide event text while preserving accessible names; picker targets remain at least 44px.
258
+
259
+ `DayAgenda` takes a local `date` (`YYYY-MM-DD`) and `AgendaEvent[]` with `id`, `title`, optional `description`, `startMinute` and `endMinute`. Minutes are relative to that day's midnight. Cross-day intervals show continuation; missing times remain pending and missing ends remain points. Its vertical timeline uses a piecewise scale for readable short events and separate lanes for overlaps. `selectedEvent`, `onEventSelect`, `onPreviousDay` and `onNextDay` provide interaction. Supply localized labels, `locale` and optional `timeZoneLabel`; no time zones or flight durations are inferred.
260
+
261
+ `JourneyMap` accepts `places`, optional GeoJSON `geography`, `routes`, `selectedPlace`, `onPlaceSelect` and required `attribution`. Place/route coordinates use latitude then longitude; GeoJSON retains longitude then latitude. Leaflet is loaded only in the browser. The map supports pan, zoom and reset, with an equivalent place-button list. It does not request location permissions, fetch map tiles, calculate routes or infer travel times. Demo geography is illustrative, not navigation data. Use `labels` to localize controls and failure feedback, and import `@inkra/react/styles.css` for all required map styles.
262
+
263
+ ## Pencil Primitives
264
+
265
+ `PencilSurface` renders pencil material behind semantic DOM content. Available variants are `paper`, `action`, `quiet`, `badge`, `outline`, `danger`, `ghost`, `selection`, `wash`, and `full`. Use `selection` for the transparent selected-tab wash; it intentionally has no paper fill, outline, or bleed.
266
+
267
+ `PencilText` keeps its string in the DOM for selection, copy, search, and assistive technology. Browser enhancement paints the same glyph geometry with the traced C lettering preset; unavailable canvas or font loading leaves the readable DOM fallback visible.
268
+
269
+ `PencilIcon` accepts familiar SVG path data and deposits pigment along sampled geometry. See `docs/pencil-contract.md` in the workspace for the current alpha contract.
270
+
271
+ `PencilLine` is an aria-hidden canvas decoration using the same pressure, grain, breaks, and repeated-pass renderer as surfaces and icons. It defaults to horizontal and supports a native vertical stroke without rotating a horizontal bitmap:
272
+
273
+ ```tsx
274
+ <PencilLine seed="notes-divider" className="notes-divider" />
275
+ <PencilLine orientation="vertical" seed="sidebar-divider" className="sidebar-divider" />
276
+ ```
277
+
278
+ ### Rendering tokens
279
+
280
+ `PencilText` reads `--inkra-letter-variation`, `--inkra-letter-breaks`,
281
+ `--inkra-letter-outline`, `--inkra-letter-fill`, and `--inkra-letter-grain` from
282
+ its computed style. Values are clamped to 0-1; missing or invalid values use the
283
+ default lettering preset (0.20, 0.50, 0.90, 0.90, 0.60 respectively). Apply these tokens to an ancestor to customize all
284
+ text within that scope without changing layout, selectable text, or labels.
285
+
286
+ `PencilLine` reads `--inkra-stroke-width` (0.3-3) and
287
+ `--inkra-stroke-amplitude` (0-4) when `weight` or `roughness` is omitted.
288
+ Explicit props take precedence. These tokens also scale surface outlines and icon
289
+ strokes relative to their original presets, including button/card/input borders.
290
+ The default values are 2 and 1.5. They do not change layout or text lettering.
291
+
292
+ Surface fills read `--inkra-wash-strength` (0-1, default 0.8),
293
+ `--inkra-wash-spacing` (1-6, default 2), `--inkra-wash-roughness` (0-6,
294
+ default 3), and `--inkra-wash-angle` (-85 to 85 degrees, default -45).
295
+ Strength scales each variant's original light/dark pigment intensity relative to
296
+ the original 0.48 reference. These settings affect button fills, selections
297
+ and swatches, not paper or lettering. Selection reveals follow the chosen angle.
298
+
299
+ Only self/ancestor appearance changes invalidate decoration, not unrelated theme
300
+ subtrees. Unchanged computed paint settings are reused, including when a dialog
301
+ changes body pointer events or scroll locks. Rendering settings are part of the
302
+ bounded texture cache keys. No page remount is needed. Forced-color, print, and
303
+ unavailable-canvas fallbacks remain intact.
304
+
305
+ ## Font license
306
+
307
+ The package includes `Trip Map Hand`, a modified web subset of Yozai Light v0.868, under the SIL Open Font License 1.1. The font is not covered by Inkra's MIT software license. See `THIRD_PARTY_NOTICES.md` and `Yozai-OFL.txt` in the package.
308
+
309
+ ## Development
310
+
311
+ ```sh
312
+ npm run typecheck
313
+ npm test
314
+ npm run pack:check
315
+ ```
316
+
317
+ `npm test` builds the package before running the SSR and determinism tests. `pack:check` prints the files that would be included without publishing anything.
@@ -0,0 +1,39 @@
1
+ # Third-Party Notices
2
+
3
+ ## Trip Map Hand font subset
4
+
5
+ `dist/assets/ui-hand-yozai.woff2` is a web subset of Yozai Light v0.868 by LXGW, derived from work by Y.Oz. The subset uses the modified family name `Trip Map Hand` and is included to provide the handwriting geometry used by Inkra's pencil-text renderer.
6
+
7
+ The font software is licensed under the SIL Open Font License 1.1. The complete license and copyright notice are included in `Yozai-OFL.txt`. The font is not covered by Inkra's MIT software license.
8
+
9
+ Source: Yozai Font release v0.868.
10
+
11
+ ## Leaflet
12
+
13
+ The distributed stylesheet includes Leaflet CSS and supporting image assets.
14
+
15
+ BSD 2-Clause License
16
+
17
+ Copyright (c) 2010-2023, Volodymyr Agafonkin
18
+ Copyright (c) 2010-2011, CloudMade
19
+ All rights reserved.
20
+
21
+ Redistribution and use in source and binary forms, with or without
22
+ modification, are permitted provided that the following conditions are met:
23
+
24
+ 1. Redistributions of source code must retain the above copyright notice, this
25
+ list of conditions and the following disclaimer.
26
+ 2. Redistributions in binary form must reproduce the above copyright notice,
27
+ this list of conditions and the following disclaimer in the documentation
28
+ and/or other materials provided with the distribution.
29
+
30
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
34
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
36
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
37
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
38
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/Yozai-OFL.txt ADDED
@@ -0,0 +1,94 @@
1
+ Copyright 2020, 2024 LXGW (https://github.com/lxgw/yozai-font)
2
+ Copyright (c) 2021-03-07, Y.Oz (Y.OzVox) (http://yozvox.web.fc2.com), words containing "Y.Oz" or "YOz" are reserved as font names.
3
+
4
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
5
+ This license is copied below, and is also available with a FAQ at:
6
+ http://scripts.sil.org/OFL
7
+
8
+
9
+ -----------------------------------------------------------
10
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11
+ -----------------------------------------------------------
12
+
13
+ PREAMBLE
14
+ The goals of the Open Font License (OFL) are to stimulate worldwide
15
+ development of collaborative font projects, to support the font creation
16
+ efforts of academic and linguistic communities, and to provide a free and
17
+ open framework in which fonts may be shared and improved in partnership
18
+ with others.
19
+
20
+ The OFL allows the licensed fonts to be used, studied, modified and
21
+ redistributed freely as long as they are not sold by themselves. The
22
+ fonts, including any derivative works, can be bundled, embedded,
23
+ redistributed and/or sold with any software provided that any reserved
24
+ names are not used by derivative works. The fonts and derivatives,
25
+ however, cannot be released under any other type of license. The
26
+ requirement for fonts to remain under this license does not apply
27
+ to any document created using the fonts or their derivatives.
28
+
29
+ DEFINITIONS
30
+ "Font Software" refers to the set of files released by the Copyright
31
+ Holder(s) under this license and clearly marked as such. This may
32
+ include source files, build scripts and documentation.
33
+
34
+ "Reserved Font Name" refers to any names specified as such after the
35
+ copyright statement(s).
36
+
37
+ "Original Version" refers to the collection of Font Software components as
38
+ distributed by the Copyright Holder(s).
39
+
40
+ "Modified Version" refers to any derivative made by adding to, deleting,
41
+ or substituting -- in part or in whole -- any of the components of the
42
+ Original Version, by changing formats or by porting the Font Software to a
43
+ new environment.
44
+
45
+ "Author" refers to any designer, engineer, programmer, technical
46
+ writer or other person who contributed to the Font Software.
47
+
48
+ PERMISSION & CONDITIONS
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
51
+ redistribute, and sell modified and unmodified copies of the Font
52
+ Software, subject to the following conditions:
53
+
54
+ 1) Neither the Font Software nor any of its individual components,
55
+ in Original or Modified Versions, may be sold by itself.
56
+
57
+ 2) Original or Modified Versions of the Font Software may be bundled,
58
+ redistributed and/or sold with any software, provided that each copy
59
+ contains the above copyright notice and this license. These can be
60
+ included either as stand-alone text files, human-readable headers or
61
+ in the appropriate machine-readable metadata fields within text or
62
+ binary files as long as those fields can be easily viewed by the user.
63
+
64
+ 3) No Modified Version of the Font Software may use the Reserved Font
65
+ Name(s) unless explicit written permission is granted by the corresponding
66
+ Copyright Holder. This restriction only applies to the primary font name as
67
+ presented to the users.
68
+
69
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
70
+ Software shall not be used to promote, endorse or advertise any
71
+ Modified Version, except to acknowledge the contribution(s) of the
72
+ Copyright Holder(s) and the Author(s) or with their explicit written
73
+ permission.
74
+
75
+ 5) The Font Software, modified or unmodified, in part or in whole,
76
+ must be distributed entirely under this license, and must not be
77
+ distributed under any other license. The requirement for fonts to
78
+ remain under this license does not apply to any document created
79
+ using the Font Software.
80
+
81
+ TERMINATION
82
+ This license becomes null and void if any of the above conditions are
83
+ not met.
84
+
85
+ DISCLAIMER
86
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
88
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
89
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
90
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
91
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
92
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
93
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
94
+ OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,36 @@
1
+ import type { AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
2
+ export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link';
3
+ export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'icon-xs' | 'icon-sm' | 'icon' | 'icon-lg';
4
+ export type ButtonGroupOrientation = 'horizontal' | 'vertical';
5
+ export type ButtonAlign = 'start' | 'center' | 'end';
6
+ export type ButtonLoadingPosition = 'start' | 'center' | 'end';
7
+ interface SharedButtonProps {
8
+ variant?: ButtonVariant;
9
+ size?: ButtonSize;
10
+ loading?: boolean;
11
+ seed?: string | number;
12
+ startIcon?: ReactNode;
13
+ endIcon?: ReactNode;
14
+ fullWidth?: boolean;
15
+ align?: ButtonAlign;
16
+ loadingPosition?: ButtonLoadingPosition;
17
+ loadingIndicator?: ReactNode;
18
+ }
19
+ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, SharedButtonProps {
20
+ }
21
+ export interface ButtonLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'children'>, SharedButtonProps {
22
+ children?: ReactNode;
23
+ disabled?: boolean;
24
+ }
25
+ export interface ButtonGroupProps extends HTMLAttributes<HTMLDivElement> {
26
+ orientation?: ButtonGroupOrientation;
27
+ size?: ButtonSize;
28
+ variant?: ButtonVariant;
29
+ disabled?: boolean;
30
+ fullWidth?: boolean;
31
+ equalWidth?: boolean;
32
+ }
33
+ export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
34
+ export declare const ButtonLink: import("react").ForwardRefExoticComponent<ButtonLinkProps & import("react").RefAttributes<HTMLAnchorElement>>;
35
+ export declare const ButtonGroup: import("react").ForwardRefExoticComponent<ButtonGroupProps & import("react").RefAttributes<HTMLDivElement>>;
36
+ export {};
package/dist/Button.js ADDED
@@ -0,0 +1,89 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { createContext, forwardRef, useContext, useId } from 'react';
3
+ import { PencilSurface } from './PencilSurface.js';
4
+ import { PencilText } from './PencilText.js';
5
+ import { PencilLine } from './PencilLine.js';
6
+ import { InputActionContext } from './internal/InputActionContext.js';
7
+ const ButtonGroupContext = createContext(null);
8
+ function surfaceVariant(variant) {
9
+ if (variant === 'primary')
10
+ return 'action';
11
+ if (variant === 'secondary' || variant === 'ghost')
12
+ return 'quiet';
13
+ if (variant === 'outline')
14
+ return 'outline';
15
+ if (variant === 'destructive')
16
+ return 'danger';
17
+ return 'ghost';
18
+ }
19
+ function classNames(className) {
20
+ return ['inkra-button', className].filter(Boolean).join(' ');
21
+ }
22
+ function accessibleLabel(ariaLabel, loading, children) {
23
+ return ariaLabel ?? (loading && typeof children === 'string' ? children : undefined);
24
+ }
25
+ function ButtonVisual({ children, endIcon, loading, loadingPosition, loadingIndicator, seed, startIcon, variant }) {
26
+ const group = useContext(ButtonGroupContext);
27
+ const inputAction = useContext(InputActionContext);
28
+ const sharedOutline = group !== null || inputAction;
29
+ const indicator = _jsx("span", { "aria-hidden": "true", className: "inkra-button__loading-indicator", children: loadingIndicator ?? _jsx("span", { className: "inkra-button__spinner" }) });
30
+ const iconSlot = (side, icon) => {
31
+ const reserved = loadingPosition === side;
32
+ if (icon == null && !reserved)
33
+ return null;
34
+ return _jsxs("span", { "aria-hidden": "true", className: `inkra-button__icon inkra-button__icon--${side}`, "data-loading-slot": reserved || undefined, children: [_jsx("span", { className: "inkra-button__icon-content", children: icon }), reserved && loading ? indicator : null] });
35
+ };
36
+ const label = typeof children === 'string'
37
+ ? _jsx(PencilText, { seed: `${seed}-label`, children: children })
38
+ : children == null ? null : _jsx("span", { children: children });
39
+ return (_jsxs(_Fragment, { children: [_jsx(PencilSurface, { as: "span", className: "inkra-button__surface", clipContent: sharedOutline, radius: sharedOutline ? 0 : undefined, seed: seed, variant: inputAction ? 'ghost' : surfaceVariant(variant), children: _jsxs("span", { className: "inkra-button__content", children: [loading && loadingPosition === 'center' ? indicator : null, iconSlot('start', startIcon), label == null ? null : _jsxs("span", { className: "inkra-button__label", children: [label, variant === 'link' ? _jsx(PencilLine, { className: "inkra-button__underline", seed: `${seed}-underline`, weight: 0.9 }) : null] }), iconSlot('end', endIcon)] }) }), _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-button__focus-ring", variant: "outline", seed: `${seed}-focus` }), group === null ? null : _jsx(PencilLine, { className: "inkra-button-group__divider", orientation: group.orientation === 'horizontal' ? 'vertical' : 'horizontal', seed: `${seed}-group-divider` })] }));
40
+ }
41
+ export const Button = forwardRef(function Button({ children, 'aria-label': ariaLabel, className, disabled = false, endIcon, loading = false, loadingPosition = 'center', loadingIndicator, fullWidth = false, align = 'center', seed, size: sizeProp, startIcon, type = 'button', variant: variantProp, ...props }, ref) {
42
+ const id = useId();
43
+ const group = useContext(ButtonGroupContext);
44
+ const size = sizeProp ?? group?.size ?? 'md';
45
+ const variant = variantProp ?? group?.variant ?? 'primary';
46
+ const position = size.startsWith('icon') ? 'center' : loadingPosition;
47
+ const resolvedSeed = seed ?? `inkra-button-${id}`;
48
+ return (_jsx("button", { ...props, "aria-label": accessibleLabel(ariaLabel, loading, children), "aria-busy": loading || undefined, className: classNames(className), "data-loading": loading || undefined, "data-loading-position": position, "data-full-width": fullWidth && !size.startsWith('icon') || undefined, "data-align": align, "data-size": size, "data-variant": variant, disabled: disabled || loading || group?.disabled, ref: ref, type: type, children: _jsx(ButtonVisual, { endIcon: endIcon, loading: loading, loadingPosition: position, loadingIndicator: loadingIndicator, seed: resolvedSeed, startIcon: startIcon, variant: variant, children: children }) }));
49
+ });
50
+ export const ButtonLink = forwardRef(function ButtonLink({ children, 'aria-label': ariaLabel, className, disabled = false, endIcon, href, loading = false, loadingPosition = 'center', loadingIndicator, fullWidth = false, align = 'center', onAuxClick, onClick, onKeyDown, seed, size: sizeProp, startIcon, tabIndex, variant: variantProp, ...props }, ref) {
51
+ const id = useId();
52
+ const group = useContext(ButtonGroupContext);
53
+ const size = sizeProp ?? group?.size ?? 'md';
54
+ const variant = variantProp ?? group?.variant ?? 'primary';
55
+ const position = size.startsWith('icon') ? 'center' : loadingPosition;
56
+ const resolvedSeed = seed ?? `inkra-button-link-${id}`;
57
+ const interactionDisabled = disabled || loading || group?.disabled;
58
+ const handleClick = (event) => {
59
+ if (interactionDisabled) {
60
+ event.preventDefault();
61
+ event.stopPropagation();
62
+ return;
63
+ }
64
+ onClick?.(event);
65
+ };
66
+ const handleAuxClick = (event) => {
67
+ if (interactionDisabled) {
68
+ event.preventDefault();
69
+ event.stopPropagation();
70
+ return;
71
+ }
72
+ onAuxClick?.(event);
73
+ };
74
+ const handleKeyDown = (event) => {
75
+ if (interactionDisabled) {
76
+ if (event.key === 'Enter' || event.key === ' ')
77
+ event.preventDefault();
78
+ event.stopPropagation();
79
+ return;
80
+ }
81
+ onKeyDown?.(event);
82
+ };
83
+ return (_jsx("a", { ...props, "aria-label": accessibleLabel(ariaLabel, loading, children), "aria-busy": loading || undefined, "aria-disabled": interactionDisabled || undefined, className: classNames(className), "data-disabled": interactionDisabled || undefined, "data-loading": loading || undefined, "data-loading-position": position, "data-full-width": fullWidth && !size.startsWith('icon') || undefined, "data-align": align, "data-size": size, "data-variant": variant, href: interactionDisabled ? undefined : href, onAuxClick: handleAuxClick, onClick: handleClick, onKeyDown: handleKeyDown, ref: ref, tabIndex: interactionDisabled ? -1 : tabIndex, children: _jsx(ButtonVisual, { endIcon: endIcon, loading: loading, loadingPosition: position, loadingIndicator: loadingIndicator, seed: resolvedSeed, startIcon: startIcon, variant: variant, children: children }) }));
84
+ });
85
+ export const ButtonGroup = forwardRef(function ButtonGroup({ children, className, orientation = 'horizontal', size, variant, disabled = false, fullWidth = false, equalWidth = false, ...props }, ref) {
86
+ const classes = ['inkra-button-group', className].filter(Boolean).join(' ');
87
+ const id = useId();
88
+ return (_jsx(ButtonGroupContext.Provider, { value: { orientation, size, variant, disabled }, children: _jsx(PencilSurface, { ...props, className: classes, "data-orientation": orientation, "data-full-width": fullWidth || undefined, "data-equal-width": equalWidth || undefined, "aria-disabled": disabled || undefined, radius: 8, ref: ref, role: "group", seed: `inkra-button-group-${id}`, variant: "outline", children: children }) }));
89
+ });
@@ -0,0 +1,22 @@
1
+ import type { CanvasHTMLAttributes } from 'react';
2
+ export interface PencilIconPath {
3
+ d: string;
4
+ mode?: 'stroke' | 'wash' | 'scuff';
5
+ }
6
+ export interface PencilIconBadge {
7
+ paths: readonly PencilIconPath[];
8
+ sourceSize?: number;
9
+ position?: 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
10
+ /** Relative canvas size, clamped to 0.35-0.65. Defaults to 0.55. */
11
+ scale?: number;
12
+ }
13
+ export interface PencilIconProps extends Omit<CanvasHTMLAttributes<HTMLCanvasElement>, 'children' | 'height' | 'width'> {
14
+ paths: readonly PencilIconPath[];
15
+ badge?: PencilIconBadge;
16
+ seed?: string | number;
17
+ size?: number;
18
+ sourceSize?: number;
19
+ active?: boolean;
20
+ decorative?: boolean;
21
+ }
22
+ export declare const PencilIcon: import("react").ForwardRefExoticComponent<PencilIconProps & import("react").RefAttributes<HTMLCanvasElement>>;