@graphty/compact-mantine 0.8.19 → 0.9.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 +1 -1
- package/README.md +523 -201
- package/dist/fonts/LICENSE-Inter.txt +93 -0
- package/dist/index.cjs +4044 -1
- package/dist/index.d.cts +2282 -957
- package/dist/index.d.ts +2282 -957
- package/dist/index.js +12161 -5021
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -6,17 +6,21 @@
|
|
|
6
6
|
|
|
7
7
|
Compact components and a compact theme for [Mantine 8](https://mantine.dev), for
|
|
8
8
|
interfaces where screen space is the scarce resource: property panels,
|
|
9
|
-
inspectors, sidebars, editors and dashboards.
|
|
9
|
+
inspectors, sidebars, editors and dashboards. The look is measured from the
|
|
10
|
+
Figma editor's own interface, in its light and its dark theme: the same 24px
|
|
11
|
+
controls, the same 11px Inter, the same colors, radii, shadows and timing.
|
|
10
12
|
|
|
11
13
|
It gives you two things, and you can take either one on its own:
|
|
12
14
|
|
|
13
|
-
1. **A theme.** Drop `compactTheme` into your `MantineProvider` and
|
|
14
|
-
components render
|
|
15
|
-
spacing -- with no `size` prop anywhere in your code.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
1. **A theme.** Drop `compactTheme` into your `MantineProvider` and the Mantine
|
|
16
|
+
components render dense and Figma-accurate -- 24px controls, 11px text,
|
|
17
|
+
tighter spacing -- with no `size` prop anywhere in your code. The theme ships
|
|
18
|
+
its own typeface (Inter) and stylesheet, so there is nothing else to set up.
|
|
19
|
+
2. **A component library.** Components built for a narrow column and an editor
|
|
20
|
+
around it: rows that put a value and its label on one 32px line, charts that
|
|
21
|
+
fit in the height of a line of text, a virtualized data table, a
|
|
22
|
+
floating-panel system, and the editor shell -- a floating toolbar, a
|
|
23
|
+
navigation rail, a quick actions palette and a keyboard shortcuts sheet.
|
|
20
24
|
|
|
21
25
|
Everything is typed, translatable, keyboard-operable, screen-reader-tested,
|
|
22
26
|
works in light and dark schemes, and works in right-to-left languages.
|
|
@@ -29,11 +33,18 @@ works in light and dark schemes, and works in right-to-left languages.
|
|
|
29
33
|
- [Quick start](#quick-start)
|
|
30
34
|
- [The compact theme](#the-compact-theme)
|
|
31
35
|
- [The components](#the-components)
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
36
|
+
- [Panels and rows](#panels-and-rows)
|
|
37
|
+
- [Inputs](#inputs)
|
|
38
|
+
- [Selection](#selection)
|
|
39
|
+
- [Color](#color)
|
|
40
|
+
- [Actions](#actions)
|
|
35
41
|
- [Floating panels](#floating-panels)
|
|
42
|
+
- [Other overlays](#other-overlays)
|
|
43
|
+
- [Lists and trees](#lists-and-trees)
|
|
44
|
+
- [Data display](#data-display)
|
|
45
|
+
- [The editor shell](#the-editor-shell)
|
|
36
46
|
- [Glyphs](#glyphs)
|
|
47
|
+
- [Themed Mantine components](#themed-mantine-components)
|
|
37
48
|
- [A worked example](#a-worked-example)
|
|
38
49
|
- [The panel grid](#the-panel-grid)
|
|
39
50
|
- [Handling events](#handling-events)
|
|
@@ -43,6 +54,8 @@ works in light and dark schemes, and works in right-to-left languages.
|
|
|
43
54
|
- [Accessibility](#accessibility)
|
|
44
55
|
- [TypeScript](#typescript)
|
|
45
56
|
- [Which one should I use?](#which-one-should-i-use)
|
|
57
|
+
- [Use these instead of Mantine's](#use-these-instead-of-mantines)
|
|
58
|
+
- [Breaking changes in the Figma release](#breaking-changes-in-the-figma-release)
|
|
46
59
|
- [Contributing](#contributing)
|
|
47
60
|
- [License](#license)
|
|
48
61
|
|
|
@@ -76,8 +89,8 @@ export function App() {
|
|
|
76
89
|
<TextInput label="Project name" />
|
|
77
90
|
<Button>Save</Button>
|
|
78
91
|
|
|
79
|
-
{/* This package's own components, for a
|
|
80
|
-
<div style={{ width:
|
|
92
|
+
{/* This package's own components, for a 240px panel. */}
|
|
93
|
+
<div style={{ width: 240 }}>
|
|
81
94
|
<ControlSection label="Node size">
|
|
82
95
|
<FieldRow>
|
|
83
96
|
<PanelField label="Smallest" glyph="sizeSmallest" defaultValue="1.0" />
|
|
@@ -91,56 +104,134 @@ export function App() {
|
|
|
91
104
|
}
|
|
92
105
|
```
|
|
93
106
|
|
|
94
|
-
That is the whole setup
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
That is the whole setup: the theme puts its stylesheet (the Inter typeface, the
|
|
108
|
+
color tokens and the component rules) on the page the first time a themed
|
|
109
|
+
component renders. Three further pieces are optional and are introduced where
|
|
110
|
+
they matter: [`Tooltip.Group`](#tooltips-wrap-your-app-once) around your app for
|
|
111
|
+
Figma's tooltip timing, [`PopoutManager`](#floating-panels) if you use pop-outs,
|
|
112
|
+
and [`LabelsProvider`](#internationalization) if you translate the library's
|
|
97
113
|
strings.
|
|
98
114
|
|
|
99
115
|
## The compact theme
|
|
100
116
|
|
|
101
|
-
`compactTheme` is a complete Mantine theme
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
| `
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
your own
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
`compactTheme` is a complete Mantine theme, already merged with Mantine's
|
|
118
|
+
default, that draws every component the way the Figma editor draws its own.
|
|
119
|
+
Light and dark are Mantine's own color scheme (`defaultColorScheme`,
|
|
120
|
+
`forceColorScheme`, `useMantineColorScheme`); one theme serves both, and the dark
|
|
121
|
+
scheme uses Figma's neutral grays (#2c2c2c panels, #383838 fields).
|
|
122
|
+
|
|
123
|
+
| Token | Values |
|
|
124
|
+
| --------------------------- | ------------------------------------------------------- |
|
|
125
|
+
| `fontSizes` / `lineHeights` | xs 9/14, sm 11/16, md 13/22, lg 15/25, xl 24/32 |
|
|
126
|
+
| `spacing` | xs 4px, sm 8px, md 8px, lg 12px, xl 16px |
|
|
127
|
+
| `radius` | xs 2px, sm 5px, md 5px, lg 13px, xl 13px |
|
|
128
|
+
| `shadows` | xs to xl map onto Figma's five elevations |
|
|
129
|
+
| `primaryColor` | `brand`: #0d99ff for filled surfaces, #007be5 for links |
|
|
130
|
+
|
|
131
|
+
Every color is a CSS custom property named `--cm-*` (for example `--cm-bg`,
|
|
132
|
+
`--cm-text-secondary`, `--cm-bg-brand`), written with the CSS `light-dark()`
|
|
133
|
+
function, so it resolves from the color scheme of the element that uses it. A
|
|
134
|
+
subtree that has to render dark inside a light app -- a menu, a tooltip, the
|
|
135
|
+
shortcuts sheet -- sets `color-scheme: dark` and every token inside it follows.
|
|
136
|
+
Read the tokens in your own CSS; `PANEL_INK` (see [the panel grid](#the-panel-grid))
|
|
137
|
+
names the ones a panel row uses.
|
|
138
|
+
|
|
139
|
+
Controls are 24px tall with an 11px face. Fields are drawn filled and without a
|
|
140
|
+
border at rest, outlined on hover and ringed on focus. The focus ring is Figma's:
|
|
141
|
+
1px, keyboard focus only (number and text fields ring on any focus), inside a
|
|
142
|
+
field and outside a button. Menus, list boxes and tooltips are dark in both
|
|
143
|
+
schemes. Overlays open and close in one frame, with no fade.
|
|
144
|
+
|
|
145
|
+
### The bundled typeface
|
|
146
|
+
|
|
147
|
+
The theme sets Inter (the variable font, latin subset) and ships it inside the
|
|
148
|
+
package, inlined into the stylesheet, so Figma's 450, 550 and 600 weights render
|
|
149
|
+
exactly with no font setup and no network request. Inter is licensed under the
|
|
150
|
+
SIL Open Font License 1.1; the license text is in `dist/fonts/LICENSE-Inter.txt`.
|
|
151
|
+
To use another face, set `fontFamily` in a theme merged over this one.
|
|
152
|
+
|
|
153
|
+
### `createCompactTheme` and the WCAG AA option
|
|
154
|
+
|
|
155
|
+
`compactTheme` is exact Figma. Figma's secondary text, field boundaries and
|
|
156
|
+
checkbox edges fall short of WCAG 2.2 AA contrast, so the theme has an option
|
|
157
|
+
that raises them:
|
|
123
158
|
|
|
124
|
-
|
|
159
|
+
```tsx
|
|
160
|
+
import { createCompactTheme } from "@graphty/compact-mantine";
|
|
161
|
+
|
|
162
|
+
<MantineProvider theme={createCompactTheme({ highContrast: true })}>{children}</MantineProvider>;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`highContrast` changes color tokens only -- nothing moves. It darkens secondary
|
|
166
|
+
text and icons (50% to 55% black, 4.7:1), raises checkbox, switch and field
|
|
167
|
+
boundaries to 3:1 (fields gain a 1px inside edge), gives the selected segment of
|
|
168
|
+
a segmented control a 3:1 edge, and darkens placeholder text, the focus ring,
|
|
169
|
+
links and the brand, danger and success fills to the next darker color in
|
|
170
|
+
Figma's own palette. Dividers stay as Figma draws them. `createCompactTheme()`
|
|
171
|
+
with no options is `compactThemeOverride`, and the options it was built with are
|
|
172
|
+
published on `theme.other.compact`.
|
|
173
|
+
|
|
174
|
+
### Tooltips: wrap your app once
|
|
175
|
+
|
|
176
|
+
Tooltips follow Figma's timing: the first one opens after 1000ms, and while one
|
|
177
|
+
is showing, or for 300ms after it closes, the next one opens at once. That
|
|
178
|
+
hand-off needs Mantine's `Tooltip.Group` around everything that has tooltips,
|
|
179
|
+
and a theme cannot add a provider, so wrap your app once:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
import { MantineProvider, Tooltip } from "@mantine/core";
|
|
183
|
+
|
|
184
|
+
<MantineProvider theme={compactTheme}>
|
|
185
|
+
<Tooltip.Group>
|
|
186
|
+
<App />
|
|
187
|
+
</Tooltip.Group>
|
|
188
|
+
</MantineProvider>;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The theme gives `Tooltip.Group` the right delays, so it needs no props. Do not
|
|
192
|
+
nest a second group inside: tooltips inside it would stop sharing the warmth.
|
|
193
|
+
No component in this package nests one, so the warm hand-off crosses the whole
|
|
194
|
+
shell: from a toolbar tooltip to a rail button, to the help button, and back.
|
|
195
|
+
The price is that inside a group Mantine ignores a tooltip's own `openDelay`
|
|
196
|
+
and `closeDelay`. Figma opens rail tooltips after 500ms and shows and hides the
|
|
197
|
+
help button's at once; `RailButton` and `HelpButton` keep those timings when
|
|
198
|
+
there is no group, and take the group's 1000ms / 300ms when cold inside one.
|
|
199
|
+
Tooltips keep their `aria-describedby` link, so screen readers still hear them.
|
|
200
|
+
|
|
201
|
+
### Stylesheet, SSR and shadow roots
|
|
202
|
+
|
|
203
|
+
The stylesheet is injected into `document.head` when the theme is created
|
|
204
|
+
(and again, if something removed it, when a themed component renders). For server rendering, put `compactGlobalCss()` (or
|
|
205
|
+
`compactGlobalCss({ highContrast: true })`) in a `<style>` in your document head.
|
|
206
|
+
For a shadow root, call `ensureCompactStyles()` or append `compactGlobalCss()` to
|
|
207
|
+
the root yourself. Mantine's own `@mantine/core/styles.css` is still required,
|
|
208
|
+
imported before this package's stylesheet as in the quick start.
|
|
125
209
|
|
|
126
|
-
|
|
127
|
-
along a compact scale: an input field is 20, 24, 30, 36 and 44px tall at `xs`,
|
|
128
|
-
`sm` (the default), `md`, `lg` and `xl`.
|
|
210
|
+
### Components the theme restyles
|
|
129
211
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
|
|
|
137
|
-
|
|
|
138
|
-
|
|
|
212
|
+
Pass no `size` prop and these render compact. Pass `size="md"` or `size="lg"`
|
|
213
|
+
and you get larger sizes back.
|
|
214
|
+
|
|
215
|
+
| Group | Components |
|
|
216
|
+
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
217
|
+
| Inputs | TextInput, NumberInput, Select, NativeSelect, Textarea, PasswordInput, Autocomplete, MultiSelect, TagsInput, PillsInput, FileInput, JsonInput, InputClearButton |
|
|
218
|
+
| Buttons | Button, ActionIcon, CloseButton |
|
|
219
|
+
| Controls | Switch, Checkbox, Radio, Slider, RangeSlider, SegmentedControl |
|
|
220
|
+
| Color | ColorSwatch, HueSlider, AlphaSlider |
|
|
221
|
+
| Display | Badge, Text, Avatar, Avatar.Group, ThemeIcon, Indicator, Kbd, Pill |
|
|
222
|
+
| Navigation | Tabs, NavLink, Pagination, Stepper, Anchor, Burger |
|
|
223
|
+
| Feedback | Loader, Progress, RingProgress, Notification |
|
|
224
|
+
| Overlays | Menu, Tooltip, Tooltip.Group, Popover, HoverCard, Modal, ScrollArea |
|
|
225
|
+
|
|
226
|
+
A few defaults are worth knowing: `Badge` defaults to the outlined look
|
|
227
|
+
(`variant="filled"` and `"light"` are the brand looks); `Kbd` is Figma's dark key
|
|
228
|
+
cap in both schemes (`size="md"` for the large cap, `variant="inline"` for a
|
|
229
|
+
light cap in running text, `mod={{ active: true }}` to light it); `Select`
|
|
230
|
+
defaults to the outlined trigger; `Tabs` defaults to pills.
|
|
139
231
|
|
|
140
232
|
### Making it your own
|
|
141
233
|
|
|
142
|
-
|
|
143
|
-
your own on top of it:
|
|
234
|
+
Merge your own theme on top of the compact one:
|
|
144
235
|
|
|
145
236
|
```tsx
|
|
146
237
|
import { createTheme, MantineProvider, mergeMantineTheme } from "@mantine/core";
|
|
@@ -150,7 +241,6 @@ const theme = mergeMantineTheme(
|
|
|
150
241
|
compactTheme,
|
|
151
242
|
createTheme({
|
|
152
243
|
primaryColor: "teal",
|
|
153
|
-
fontFamily: "Inter, sans-serif",
|
|
154
244
|
}),
|
|
155
245
|
);
|
|
156
246
|
|
|
@@ -158,8 +248,8 @@ const theme = mergeMantineTheme(
|
|
|
158
248
|
```
|
|
159
249
|
|
|
160
250
|
If you already build your theme from several overrides, take
|
|
161
|
-
`compactThemeOverride` instead. It is the raw
|
|
162
|
-
for `mergeThemeOverrides()`:
|
|
251
|
+
`compactThemeOverride` (or `createCompactTheme(options)`) instead. It is the raw
|
|
252
|
+
`createTheme()` result, suitable for `mergeThemeOverrides()`:
|
|
163
253
|
|
|
164
254
|
```tsx
|
|
165
255
|
import { createTheme, mergeThemeOverrides } from "@mantine/core";
|
|
@@ -168,6 +258,11 @@ import { compactThemeOverride } from "@graphty/compact-mantine";
|
|
|
168
258
|
const override = mergeThemeOverrides(compactThemeOverride, createTheme({ primaryColor: "teal" }));
|
|
169
259
|
```
|
|
170
260
|
|
|
261
|
+
Merge component extensions with care: `mergeThemeOverrides` replaces a
|
|
262
|
+
component's `vars` or `styles` function rather than composing it, so an override
|
|
263
|
+
of a component this theme already extends discards the compact treatment. Adjust
|
|
264
|
+
such a component through its props, `classNames` or the `--cm-*` tokens instead.
|
|
265
|
+
|
|
171
266
|
### A compact region inside a normal-sized app
|
|
172
267
|
|
|
173
268
|
`MantineProvider` nests, so you can keep your application at its usual size and
|
|
@@ -178,76 +273,105 @@ make one region dense -- which is the common case for a sidebar or an inspector.
|
|
|
178
273
|
<TextInput label="Normal size" />
|
|
179
274
|
|
|
180
275
|
<MantineProvider theme={compactTheme}>
|
|
181
|
-
<aside style={{ width:
|
|
276
|
+
<aside style={{ width: 240 }}>
|
|
182
277
|
<TextInput label="Compact" />
|
|
183
278
|
</aside>
|
|
184
279
|
</MantineProvider>
|
|
185
280
|
</MantineProvider>
|
|
186
281
|
```
|
|
187
282
|
|
|
188
|
-
Also exported: `compactColors` (the
|
|
189
|
-
`
|
|
190
|
-
elsewhere.
|
|
283
|
+
Also exported: `compactColors` (the palettes this theme adds), `compactDarkColors`
|
|
284
|
+
(its neutral dark scale) and `compactBrandColors` (the brand scale), if you want
|
|
285
|
+
to reuse the colors elsewhere.
|
|
191
286
|
|
|
192
287
|
## The components
|
|
193
288
|
|
|
194
|
-
|
|
195
|
-
[Storybook](https://graphty.app/storybook/compact-mantine/)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
|
206
|
-
|
|
|
207
|
-
| `
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
| `
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
|
220
|
-
|
|
|
221
|
-
| `
|
|
222
|
-
| `StyleSelect`
|
|
223
|
-
| `
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
|
233
|
-
|
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
| `
|
|
289
|
+
Every component has one page in
|
|
290
|
+
[Storybook](https://graphty.app/storybook/compact-mantine/): what it is for and when to use it
|
|
291
|
+
instead of its nearest alternative, an example whose props you can edit, every state in light
|
|
292
|
+
and dark side by side, the keyboard, and the key measurements. Every prop is also documented in
|
|
293
|
+
your editor. The groups below match the Storybook sidebar, and each group has an overview page.
|
|
294
|
+
|
|
295
|
+
### Panels and rows
|
|
296
|
+
|
|
297
|
+
The containers a property panel is built from, and the row layout every other component agrees
|
|
298
|
+
with. [Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-panels-and-rows-overview--docs)
|
|
299
|
+
|
|
300
|
+
| Component | Reach for it when |
|
|
301
|
+
| ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
302
|
+
| [`ControlSection`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-panels-and-rows-controlsection--docs) | a run of controls needs a name, a rule and a chevron that folds it away. The workhorse container for a property panel. It can show a dot when something inside it is non-default, an empty state with a single "+", an explanation bubble, and buttons of its own in the header. |
|
|
303
|
+
| [`ControlGroup`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-panels-and-rows-controlgroup--docs) | the same, but it must never fold, or it sits in a padded container such as a pop-out. |
|
|
304
|
+
| [`ControlSubGroup`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-panels-and-rows-controlsubgroup--docs) | a handful of rarely-opened settings belong under a section you already have. Quieter than a section. |
|
|
305
|
+
| [`FieldRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-panels-and-rows-fieldrow--docs) | one or two fields share a line. It owns the widths and the gaps, so a column of rows lines up. |
|
|
306
|
+
| [`CompoundRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-panels-and-rows-compoundrow--docs) | two or three values are one thing seen several ways -- a color and its opacity, a width and its unit -- and must read as one control. |
|
|
307
|
+
| [`TrailingSlot`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-panels-and-rows-trailingslot--docs) | you are laying out a row by hand and need the fixed 24px slot every row ends with. |
|
|
308
|
+
| [`ResizeHandle`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-panels-and-rows-resizehandle--docs) | a panel's edge can be dragged: pointer, arrow keys, or a double-click back to the default size. |
|
|
309
|
+
|
|
310
|
+
### Inputs
|
|
311
|
+
|
|
312
|
+
Values the reader types or picks. [Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-inputs-overview--docs)
|
|
313
|
+
|
|
314
|
+
| Component | Reach for it when |
|
|
315
|
+
| ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
316
|
+
| [`PanelField`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-inputs-panelfield--docs) | a value is typed, picked from a list or dragged in a panel row. A 24px text box, number box or select whose caption is a 16px drawing inside the box instead of a word above it. |
|
|
317
|
+
| [`Select` and `StyleSelect`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-inputs-select--docs) | one of a fixed list. Mantine's `Select` is themed as Figma's outlined trigger with a dark list; `StyleSelect` adds a reset, for a choice where `undefined` means "inherit the default". |
|
|
318
|
+
| [`StyleNumberInput`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-inputs-stylenumberinput--docs) | a number with a sensible default, where the panel should show at a glance whether the reader has overridden it. |
|
|
319
|
+
| [`SearchInput`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-inputs-searchinput--docs) | a filter or find box: a leading magnifier, a clear button and Escape to clear. |
|
|
320
|
+
| [`ComboInput`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-inputs-comboinput--docs) | a value you can type, with a chevron that opens a list of presets over the field (font size, gap, export scale). |
|
|
321
|
+
| [`VariablePill`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-inputs-variablepill--docs) | a value bound to a named variable, drawn as a pill inside a field, with a Detach button. |
|
|
322
|
+
|
|
323
|
+
### Selection
|
|
324
|
+
|
|
325
|
+
Yes or no, and one of a few. [Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-selection-overview--docs)
|
|
326
|
+
|
|
327
|
+
| Component | Reach for it when |
|
|
328
|
+
| ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
329
|
+
| [`ToggleRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-selection-togglerow--docs) | a setting is a plain yes or no that no picture could stand for. |
|
|
330
|
+
| [`ToggleRowGroup`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-selection-togglerowgroup--docs) | you have two or more of those. It packs them and warns in development if you give it only one. |
|
|
331
|
+
| [`ToggleWithContent`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-selection-togglewithcontent--docs) | a yes or no brings its own settings with it; turning it off takes them off the screen. |
|
|
332
|
+
| [`AlignmentMatrix`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-selection-alignmentmatrix--docs) | the 3 x 3 alignment grid: nine radios with two-dimensional arrow keys. |
|
|
333
|
+
|
|
334
|
+
Two to six options whose difference can be drawn are a themed Mantine `SegmentedControl` with a
|
|
335
|
+
glyph and a visually hidden word in each segment; its
|
|
336
|
+
[Storybook page](https://graphty.app/storybook/compact-mantine/?path=/docs/themed-mantine-selection-segmentedcontrol--docs) shows the recipe in a panel
|
|
337
|
+
row. (`IconGroupRow` did this until 0.9.)
|
|
338
|
+
|
|
339
|
+
### Color
|
|
340
|
+
|
|
341
|
+
[Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-color-overview--docs)
|
|
342
|
+
|
|
343
|
+
| Component | Reach for it when |
|
|
344
|
+
| ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
345
|
+
| [`CompactColorInput`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-color-compactcolorinput--docs) | a color and its opacity, on one 24px line, with Figma's picker in a pop-out. Needs a [`PopoutManager`](#floating-panels). |
|
|
346
|
+
| [`ColorPickerPanel`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-color-colorpickerpanel--docs) | you want Figma's picker itself on a surface of your own: paint type, saturation field, hue and alpha, eyedropper, swatches. |
|
|
347
|
+
| [`GradientEditor`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-color-gradienteditor--docs) | a multi-stop linear gradient: a bar with stop handles, one picker for the selected stop, positions and angle. Alone, or in `ColorPickerPanel`'s `gradient` slot. |
|
|
348
|
+
| [`RampRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-color-ramprow--docs) | a range is better drawn than described: a size wedge or a color ramp with its two endpoints. |
|
|
349
|
+
|
|
350
|
+
### Actions
|
|
351
|
+
|
|
352
|
+
[Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-actions-overview--docs)
|
|
353
|
+
|
|
354
|
+
| Component | Reach for it when |
|
|
355
|
+
| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
356
|
+
| [`ToggleIconButton`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-actions-toggleiconbutton--docs) | an icon button stays pressed (`aria-pressed`): a lock, a visibility eye. Drag across a column of them to set them all. |
|
|
357
|
+
| [`SplitButton`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-actions-splitbutton--docs) | a main action and a chevron that opens a menu of related choices. |
|
|
358
|
+
| [`AdvancedButton`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-actions-advancedbutton--docs) | a row or a section has settings most people never change. It marks itself when something behind it is no longer default. |
|
|
359
|
+
|
|
360
|
+
Variants the theme adds to Mantine buttons (pass them as `variant`): `Button` `danger`,
|
|
361
|
+
`danger-outline`, `inverse`, `success`; `ActionIcon` `joined` (inside a themed
|
|
362
|
+
`ActionIcon.Group`).
|
|
238
363
|
|
|
239
364
|
### Floating panels
|
|
240
365
|
|
|
241
|
-
A pop-out is a panel that opens beside a control, can be dragged, and can
|
|
242
|
-
|
|
366
|
+
A pop-out is a panel that opens beside a control, can be dragged, and can contain pop-outs of its
|
|
367
|
+
own. [Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-overlays-overview--docs)
|
|
243
368
|
|
|
244
|
-
**Put one `PopoutManager` high in your tree, above anything that opens a
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
supplies its own if there is none.)
|
|
369
|
+
**Put one `PopoutManager` high in your tree, above anything that opens a pop-out.** It owns the
|
|
370
|
+
shared floating layer: the stacking order, the portal the panels render into, and the dismissal
|
|
371
|
+
rules they all obey. Without it, anything that opens a pop-out throws
|
|
372
|
+
`usePopoutManagerContext must be used within a PopoutManager` on first render.
|
|
373
|
+
`CompactColorInput` uses a pop-out internally, so it needs one too. (`InfoCircle` is the
|
|
374
|
+
exception: it supplies its own if there is none.)
|
|
251
375
|
|
|
252
376
|
```tsx
|
|
253
377
|
import { Popout, PopoutButton, PopoutManager, UiGlyph } from "@graphty/compact-mantine";
|
|
@@ -258,7 +382,7 @@ import { Popout, PopoutButton, PopoutManager, UiGlyph } from "@graphty/compact-m
|
|
|
258
382
|
<Popout.Trigger>
|
|
259
383
|
<PopoutButton icon={<UiGlyph name="gear" />} aria-label="Display settings" />
|
|
260
384
|
</Popout.Trigger>
|
|
261
|
-
<Popout.Panel width={
|
|
385
|
+
<Popout.Panel width={240} header={{ variant: "title", title: "Display settings" }}>
|
|
262
386
|
<Popout.Content>{/* anything */}</Popout.Content>
|
|
263
387
|
</Popout.Panel>
|
|
264
388
|
</Popout>
|
|
@@ -266,63 +390,145 @@ import { Popout, PopoutButton, PopoutManager, UiGlyph } from "@graphty/compact-m
|
|
|
266
390
|
</PopoutManager>;
|
|
267
391
|
```
|
|
268
392
|
|
|
269
|
-
| Component
|
|
270
|
-
|
|
|
271
|
-
| `PopoutManager`
|
|
272
|
-
| `Popout`
|
|
273
|
-
| `
|
|
274
|
-
| `
|
|
275
|
-
| `
|
|
276
|
-
| `
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
393
|
+
| Component | What it is |
|
|
394
|
+
| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
395
|
+
| `PopoutManager` | The shared floating layer. Required, once, near the root. |
|
|
396
|
+
| [`Popout`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-overlays-popout--docs) | One pop-out: `Popout.Trigger` wraps the element that opens it, `Popout.Panel` is the panel (a width, an optional header or tab strip, its content), and `Popout.Anchor` wraps a sidebar so every panel opened inside it lines up with that sidebar's edge. |
|
|
397
|
+
| [`PopoutButton`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-overlays-popoutbutton--docs) | An icon button that stays lit while its panel is open. |
|
|
398
|
+
| [`InfoCircle`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-overlays-infocircle--docs) | A circled "i" that reveals an explanation on hover, focus or tap. |
|
|
399
|
+
| `usePopoutManager` | A hook returning `closeAll()` and `hasOpenPopouts()`, for closing every pop-out from a route a click-outside does not cover: a keyboard shortcut, a command palette row, a full-page sheet. |
|
|
400
|
+
| `PopoutRegion`, `usePopoutRegion` | `PopoutRegion id="..."` names a part of your shell (a panel, an inspector); `usePopoutRegion()` reads that name from anywhere inside it, including a pop-out opened there. It does not change which pop-outs may be open together. |
|
|
401
|
+
|
|
402
|
+
The rules a `PopoutManager` enforces, so you do not have to: Escape closes the innermost panel; a
|
|
403
|
+
click outside closes everything; only one root pop-out is open at a time; closing a panel closes
|
|
404
|
+
everything opened from it; focus returns to the trigger. Where a panel opens is set by two
|
|
405
|
+
props on `Popout.Panel`: `anchorX` decides which edge it lines up with (`"panel"`, `"trigger"`,
|
|
406
|
+
`"parent"`, or a ref of your own) and `anchorY` how far down it opens.
|
|
407
|
+
|
|
408
|
+
### Other overlays
|
|
409
|
+
|
|
410
|
+
| Component | What it is |
|
|
411
|
+
| --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
412
|
+
| [`ContextMenu`, `MenuCheckItem`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-overlays-menu--docs) | A dark menu opened at the pointer by a right-click, or from the keyboard with Shift+F10 or the ContextMenu key; and a checkable row for any Mantine `Menu`. |
|
|
413
|
+
| [`TooltipShortcut`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-overlays-tooltip--docs) | A tooltip label with its keyboard shortcut after it. |
|
|
414
|
+
| [`ModalFooter`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-overlays-modal--docs) | The footer row of a Mantine `Modal`: its action buttons, end-aligned. |
|
|
415
|
+
| [`Toast`, `ToastProvider`, `useToast`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-overlays-toast--docs) | Figma's dark toast: put one `ToastProvider` near the root and call `useToast()` to show one. |
|
|
416
|
+
|
|
417
|
+
### Lists and trees
|
|
418
|
+
|
|
419
|
+
Objects the reader selects, renames or moves. [Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-lists-and-trees-overview--docs)
|
|
420
|
+
|
|
421
|
+
| Component | Reach for it when |
|
|
422
|
+
| ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
423
|
+
| [`Tree`, `TreeItem`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-lists-and-trees-tree--docs) | a list of objects, nested or flat: a real ARIA tree with arrow keys, multiple selection, F2 to rename, drag to reorder, and Alt+ArrowUp / Alt+ArrowDown to move an item when you pass `onMove`. With no `children` on any item it is a flat reorderable list. The tree never edits your data: `moveTreeItem(items, move)` and `renameTreeItem(items, id, name)` apply what `onMove` and `onRename` report and return a new list. |
|
|
424
|
+
| [`PageList`, `PageRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-lists-and-trees-pagelist--docs) | flat pages with dividers, above a layer tree. |
|
|
425
|
+
| [`InlineRename`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-lists-and-trees-inlinerename--docs) | the in-place rename field a tree or page row turns into, for a row of your own. |
|
|
426
|
+
| [`ResultRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-lists-and-trees-resultrow--docs) | one row of find results. |
|
|
427
|
+
|
|
428
|
+
### Data display
|
|
429
|
+
|
|
430
|
+
Readings: things the reader looks at rather than changes. [Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-overview--docs)
|
|
431
|
+
|
|
432
|
+
| Component | Reach for it when |
|
|
433
|
+
| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
434
|
+
| [`DataRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-datarow--docs) | a name and a reading on one line -- a statistic, a fact, a ranking. `selected` marks the current item of a ranking; `onClick` opens what the reading is about. For objects the reader selects, renames or moves, use `Tree` or `PageList`. |
|
|
435
|
+
| [`DataRowHeader`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-datarowheader--docs) | a run of data rows needs a caption. Give it `onSortChange` and it becomes a sort control. |
|
|
436
|
+
| [`RankChip`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-rankchip--docs) | a rank belongs beside a row: `#6`, rather than "rank 6 of 318". |
|
|
437
|
+
| [`MetricRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-metricrow--docs) | one reading has a percentile and a rank: the name, a bar filled to the percentile, the number and the chip. |
|
|
438
|
+
| [`HistogramRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-histogramrow--docs) | a distribution would otherwise be spelled as the numbers that summarize it. 64px tall. |
|
|
439
|
+
| [`SparklineRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-sparklinerow--docs) | a series is going somewhere and you want to see which way. 32px tall. |
|
|
440
|
+
| [`ProseBlock`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-proseblock--docs) | the panel has to say something in words: a reading, a caveat, a record of the last run. |
|
|
441
|
+
| [`ActionRow`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-actionrow--docs) | a row reports a state and offers verbs beside it. |
|
|
442
|
+
| [`DataTable`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-data-display-datatable--docs) | you have columns rather than rows: thousands of them, sortable, searchable, selectable, virtualized. |
|
|
443
|
+
|
|
444
|
+
### The editor shell
|
|
445
|
+
|
|
446
|
+
The chrome around an editor's canvas, as Figma draws it. Each one is a real toolbar, dialog or
|
|
447
|
+
tab list: one Tab stop, arrow keys inside, names for screen readers.
|
|
448
|
+
[Overview](https://graphty.app/storybook/compact-mantine/?path=/docs/components-app-shell-overview--docs)
|
|
449
|
+
|
|
450
|
+
| Component | What it is |
|
|
451
|
+
| ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
452
|
+
| [`Toolbar`, `ToolButton`, `ToolGroup`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-app-shell-toolbar--docs) | The floating bottom toolbar (48 tall, 13px corners, `floating` pins it bottom center), its 32 x 32 tools (`label`, `icon`, `shortcut`, `selected`), and a tool with a flyout of related tools whose face shows the last one picked. |
|
|
453
|
+
| [`SecondaryToolbar`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-app-shell-secondarytoolbar--docs) | The contextual 40-tall bar above the toolbar while a mode is active. |
|
|
454
|
+
| [`NavRail`, `RailButton`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-app-shell-navrail--docs) | The 56-wide navigation rail at the window's edge, and its destinations: a 32 x 32 pill over a 9px caption. |
|
|
455
|
+
| [`HelpButton`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-app-shell-helpbutton--docs) | The round floating help button; its children are the items of the menu it opens. |
|
|
456
|
+
| [`ShortcutSheet`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-app-shell-shortcutsheet--docs) | The keyboard shortcuts sheet docked at the window's foot: tabs of shortcut columns with dark key caps. |
|
|
457
|
+
| [`QuickActions`](https://graphty.app/storybook/compact-mantine/?path=/docs/components-app-shell-quickactions--docs) | The quick actions palette: a search field over sectioned rows. Typing filters, arrows move, Enter runs, Escape closes. |
|
|
458
|
+
|
|
459
|
+
```tsx
|
|
460
|
+
import { SegmentedControl } from "@mantine/core";
|
|
461
|
+
import { useState } from "react";
|
|
462
|
+
import { Toolbar, ToolButton, ToolGroup } from "@graphty/compact-mantine";
|
|
463
|
+
|
|
464
|
+
function EditorToolbar() {
|
|
465
|
+
const [tool, setTool] = useState("move");
|
|
466
|
+
return (
|
|
467
|
+
<Toolbar aria-label="Editor" floating>
|
|
468
|
+
<ToolGroup label="Move tools" tools={moveTools} activeTool={tool} onToolChange={setTool} />
|
|
469
|
+
<ToolGroup label="Shape tools" tools={shapeTools} activeTool={tool} onToolChange={setTool} />
|
|
470
|
+
<ToolButton label="Actions" icon={<ActionsGlyph />} shortcut="Ctrl+K" />
|
|
471
|
+
<Toolbar.Divider />
|
|
472
|
+
<SegmentedControl variant="toolbar" data={modes} />
|
|
473
|
+
</Toolbar>
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The shell brings no glyphs of its own for your tools: pass your own icons, drawn to sit in a 24px
|
|
479
|
+
box.
|
|
289
480
|
|
|
290
481
|
### Glyphs
|
|
291
482
|
|
|
292
|
-
The premise of the row components is that a small drawing can replace a word,
|
|
293
|
-
|
|
294
|
-
|
|
483
|
+
The premise of the row components is that a small drawing can replace a word, which only works
|
|
484
|
+
if the drawings are a fixed, learnable set. Two components draw them:
|
|
485
|
+
|
|
486
|
+
- `FieldGlyph` -- the eight glyphs allowed inside a field's slot, each with a hollow and a filled
|
|
487
|
+
form. `FIELD_GLYPH_NAMES` lists them.
|
|
488
|
+
- `UiGlyph` -- the shared marks the components draw elsewhere: chevrons and carets, settings, close,
|
|
489
|
+
plus, check, warning, the alignment and transform verbs, and the layer shapes, each drawn with a
|
|
490
|
+
1px stroke at any size. `UI_GLYPH_NAMES` lists them.
|
|
295
491
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
492
|
+
Both take a `name` and an optional `size`, draw in `currentColor`, and are hidden from screen
|
|
493
|
+
readers, because the control around them carries the name. `FIELD_LETTERS` is the small set of
|
|
494
|
+
capital letters a field may show when a concept has no picture. See every drawing on the
|
|
495
|
+
[Glyphs page](https://graphty.app/storybook/compact-mantine/?path=/docs/foundations-glyphs--docs).
|
|
300
496
|
|
|
301
|
-
|
|
302
|
-
hidden from screen readers, because the control around them carries the name.
|
|
303
|
-
`FIELD_LETTERS` is the small set of capital letters a field may show in place of
|
|
304
|
-
a drawing when a concept has no picture.
|
|
497
|
+
### Themed Mantine components
|
|
305
498
|
|
|
306
|
-
|
|
307
|
-
[
|
|
499
|
+
The Mantine components the theme restyles but does not wrap have their own section in Storybook,
|
|
500
|
+
[Themed Mantine](https://graphty.app/storybook/compact-mantine/?path=/docs/themed-mantine-actions-button--docs), grouped as actions, inputs,
|
|
501
|
+
selection, navigation, feedback and surfaces. Their props are Mantine's. Looks the theme adds through
|
|
502
|
+
`variant`: `Checkbox` `neutral` (Figma's gray panel checkbox; the default is the blue dialog one),
|
|
503
|
+
`SegmentedControl` `toolbar` (the sliding mode switch) and `loose`, `Anchor` `secondary`, and
|
|
504
|
+
`Tabs` `default` (the underline tabs; pills are the default).
|
|
308
505
|
|
|
309
506
|
## A worked example
|
|
310
507
|
|
|
311
|
-
A complete section of a property panel
|
|
508
|
+
A complete section of a property panel. The scale is a themed Mantine `SegmentedControl` whose
|
|
509
|
+
segments are drawings with a visually hidden word, the recipe that replaced `IconGroupRow`.
|
|
312
510
|
|
|
313
511
|
```tsx
|
|
512
|
+
import { SegmentedControl, VisuallyHidden } from "@mantine/core";
|
|
314
513
|
import {
|
|
315
514
|
AdvancedButton,
|
|
316
515
|
ControlSection,
|
|
317
516
|
FieldGlyph,
|
|
318
517
|
FieldRow,
|
|
319
|
-
|
|
518
|
+
PANEL_GRID,
|
|
320
519
|
PanelField,
|
|
321
520
|
RampRow,
|
|
322
521
|
ToggleRow,
|
|
323
522
|
ToggleRowGroup,
|
|
523
|
+
TrailingSlot,
|
|
324
524
|
} from "@graphty/compact-mantine";
|
|
325
525
|
|
|
526
|
+
const SCALES = [
|
|
527
|
+
{ value: "sqrt", word: "Square root", glyph: "scaleSqrt" },
|
|
528
|
+
{ value: "linear", word: "Linear", glyph: "scaleLinear" },
|
|
529
|
+
{ value: "log", word: "Logarithmic", glyph: "scaleLog" },
|
|
530
|
+
] as const;
|
|
531
|
+
|
|
326
532
|
function NodeSizeSection({ openAdvanced }: { openAdvanced: () => void }) {
|
|
327
533
|
return (
|
|
328
534
|
<ControlSection
|
|
@@ -340,17 +546,32 @@ function NodeSizeSection({ openAdvanced }: { openAdvanced: () => void }) {
|
|
|
340
546
|
{/* The mapping between them, drawn at row height. */}
|
|
341
547
|
<RampRow label="Size range" min="1.0" max="4.0" variant="size" scale="sqrt" />
|
|
342
548
|
|
|
343
|
-
{/* Three options whose difference can be drawn. */}
|
|
344
|
-
<
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
549
|
+
{/* Three options whose difference can be drawn: a segmented control of pictures. */}
|
|
550
|
+
<div
|
|
551
|
+
style={{
|
|
552
|
+
display: "flex",
|
|
553
|
+
alignItems: "center",
|
|
554
|
+
gap: PANEL_GRID.TRAIL_GAP,
|
|
555
|
+
height: PANEL_GRID.ROW_PITCH,
|
|
556
|
+
}}
|
|
557
|
+
>
|
|
558
|
+
<SegmentedControl
|
|
559
|
+
fullWidth
|
|
560
|
+
aria-label="Scale"
|
|
561
|
+
defaultValue="sqrt"
|
|
562
|
+
data={SCALES.map((s) => ({
|
|
563
|
+
value: s.value,
|
|
564
|
+
label: (
|
|
565
|
+
<>
|
|
566
|
+
<FieldGlyph name={s.glyph} />
|
|
567
|
+
<VisuallyHidden>{s.word}</VisuallyHidden>
|
|
568
|
+
</>
|
|
569
|
+
),
|
|
570
|
+
}))}
|
|
571
|
+
style={{ flex: "1 1 auto", minWidth: 0 }}
|
|
572
|
+
/>
|
|
573
|
+
<TrailingSlot />
|
|
574
|
+
</div>
|
|
354
575
|
|
|
355
576
|
{/* Booleans with no picture, packed tighter than the other rows. */}
|
|
356
577
|
<ToggleRowGroup label="Node decorations">
|
|
@@ -382,12 +603,12 @@ user setting is recommended; nothing in the library requires it.
|
|
|
382
603
|
|
|
383
604
|
## The panel grid
|
|
384
605
|
|
|
385
|
-
The row components are laid out for
|
|
386
|
-
themselves from one exported object so that a column of them lines up:
|
|
606
|
+
The row components are laid out for Figma's 240px panel column, and they all
|
|
607
|
+
measure themselves from one exported object so that a column of them lines up:
|
|
387
608
|
|
|
388
609
|
```
|
|
389
|
-
16 +
|
|
390
|
-
pad
|
|
610
|
+
16 + 88 + 8 + 88 + 8 + 24 + 8 = 240
|
|
611
|
+
pad field gut field gap trail pad
|
|
391
612
|
```
|
|
392
613
|
|
|
393
614
|
`PANEL_GRID` names every number in it -- `WIDTH`, `FIELD`, `BODY`,
|
|
@@ -395,11 +616,11 @@ pad field gut field gap trail pad
|
|
|
395
616
|
match without retyping them. It is also published on the theme as
|
|
396
617
|
`theme.other.panelGrid`.
|
|
397
618
|
|
|
398
|
-
`PANEL_INK` is the matching
|
|
399
|
-
(`VALUE`, `CHROME`, `SURFACE`, `ACCENT`, `BORDER`, `SELECTED`, `
|
|
400
|
-
so on), each one a
|
|
401
|
-
own rows from it and they follow the light scheme, the dark
|
|
402
|
-
|
|
619
|
+
`PANEL_INK` is the matching color map: one entry per role a row paints
|
|
620
|
+
(`VALUE`, `CHROME`, `SURFACE`, `ACCENT`, `BORDER`, `SELECTED`, `HOVER`,
|
|
621
|
+
`FOCUS`, `DISABLED` and so on), each one a `--cm-*` token rather than a fixed
|
|
622
|
+
color. Paint your own rows from it and they follow the light scheme, the dark
|
|
623
|
+
scheme and the AA option for free.
|
|
403
624
|
|
|
404
625
|
```tsx
|
|
405
626
|
import { PANEL_GRID, PANEL_INK } from "@graphty/compact-mantine";
|
|
@@ -407,7 +628,7 @@ import { PANEL_GRID, PANEL_INK } from "@graphty/compact-mantine";
|
|
|
407
628
|
<div style={{ height: PANEL_GRID.ROW_PITCH, color: PANEL_INK.CHROME }}>Custom row</div>;
|
|
408
629
|
```
|
|
409
630
|
|
|
410
|
-
You do not have to use a
|
|
631
|
+
You do not have to use a 240px column. Nothing enforces the width; the numbers
|
|
411
632
|
are there so that the components agree with each other and with anything you
|
|
412
633
|
write beside them.
|
|
413
634
|
|
|
@@ -442,16 +663,16 @@ modifier keys, call `preventDefault()`, or find the element that was activated:
|
|
|
442
663
|
/>
|
|
443
664
|
```
|
|
444
665
|
|
|
445
|
-
Rows whose selection
|
|
666
|
+
Rows whose selection behavior depends on how they were activated -- `DataRow`,
|
|
446
667
|
`MetricRow`, `ActionRow` and `DataTable`'s rows -- get a second argument saying
|
|
447
668
|
so, rather than making you sniff the event:
|
|
448
669
|
|
|
449
670
|
```tsx
|
|
450
671
|
<DataRow
|
|
451
|
-
|
|
452
|
-
value=
|
|
672
|
+
name="Mr_Whiskers"
|
|
673
|
+
value="12"
|
|
453
674
|
onClick={(event, meta) => {
|
|
454
|
-
|
|
675
|
+
openNode(id, { inNewPanel: meta.source === "pointer" && event.shiftKey });
|
|
455
676
|
}}
|
|
456
677
|
/>
|
|
457
678
|
```
|
|
@@ -590,11 +811,20 @@ as a set of props you must remember:
|
|
|
590
811
|
|
|
591
812
|
- **Every control is a real control.** Fields are inputs, toggles are checkboxes
|
|
592
813
|
or switches, segmented options are radios, the advanced button is a button, so
|
|
593
|
-
keyboard
|
|
814
|
+
keyboard behavior, focus order and disabled semantics come from the platform.
|
|
594
815
|
- **Keyboard focus is always visible**, and pointer focus is not, so a dense
|
|
595
816
|
surface stays quiet under the mouse without giving up the focus indicator.
|
|
817
|
+
- **Exact Figma by default, AA on request.** Figma's own contrast falls short
|
|
818
|
+
of AA in a few places (secondary text, field and checkbox edges); pass
|
|
819
|
+
`createCompactTheme({ highContrast: true })` to raise exactly those. See
|
|
820
|
+
[the WCAG AA option](#createcompacttheme-and-the-wcag-aa-option).
|
|
821
|
+
- **Accessibility kept beyond Figma.** Tooltips stay linked with
|
|
822
|
+
`aria-describedby`; the toolbar, the rail and the shortcuts sheet are real
|
|
823
|
+
toolbars and tab lists with arrow-key movement; the quick actions palette is a
|
|
824
|
+
named dialog with a combobox and a list box; the shortcuts sheet closes with
|
|
825
|
+
Escape.
|
|
596
826
|
- **State is announced, not only drawn.** Expanded, checked, selected, disabled,
|
|
597
|
-
busy and current are all exposed as ARIA in addition to
|
|
827
|
+
busy and current are all exposed as ARIA in addition to color.
|
|
598
828
|
- **Charts are one named image with a hidden table behind them**, so a screen
|
|
599
829
|
reader announces the chart and can then read every value, rather than
|
|
600
830
|
announcing a run of anonymous bars. Give every chart a `label`.
|
|
@@ -605,7 +835,7 @@ as a set of props you must remember:
|
|
|
605
835
|
reader set themselves.
|
|
606
836
|
- **Hover-revealed controls also appear on focus**, stay in the document, and
|
|
607
837
|
stay in the tab order.
|
|
608
|
-
- **
|
|
838
|
+
- **Color is never the only signal.** An advanced button whose settings have
|
|
609
839
|
changed says so in its accessible name; a section with non-default values does
|
|
610
840
|
the same.
|
|
611
841
|
|
|
@@ -637,32 +867,104 @@ unknown string to them), `DataTableColumn<TRow>` describes a table column, and
|
|
|
637
867
|
|
|
638
868
|
## Which one should I use?
|
|
639
869
|
|
|
640
|
-
Some components overlap, and a few sit beside a Mantine component that looks
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
|
647
|
-
|
|
|
648
|
-
| A
|
|
649
|
-
| A
|
|
650
|
-
| A
|
|
651
|
-
| A
|
|
652
|
-
|
|
|
653
|
-
| A
|
|
654
|
-
|
|
|
655
|
-
|
|
|
656
|
-
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
870
|
+
Some components overlap, and a few sit beside a Mantine component that looks as though it would
|
|
871
|
+
do. This table names the one to reach for, and what it is being chosen over where that choice is
|
|
872
|
+
not obvious. Storybook's
|
|
873
|
+
[Choosing a component](https://graphty.app/storybook/compact-mantine/?path=/docs/introduction-choosing-a-component--docs)
|
|
874
|
+
page has the full list, group by group.
|
|
875
|
+
|
|
876
|
+
| If you have | Use | Instead of |
|
|
877
|
+
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
878
|
+
| A name and a reading on one line | `DataRow`, with the reading already formatted by `useNumberFormatter()` | `Tree` or `PageList`, which are for objects the reader selects, renames or moves |
|
|
879
|
+
| A list of objects to select, rename or reorder | `Tree` (nested, or flat when no item has `children`) or `PageList` | `DataRow`, which lost its listbox props in 0.9 |
|
|
880
|
+
| A group of controls that folds away | `ControlSection` | -- |
|
|
881
|
+
| A group of controls that must not fold, or sits in a padded container | `ControlGroup` | -- |
|
|
882
|
+
| Two or more checkboxes on their own lines | `ToggleRow`s inside a `ToggleRowGroup` | Mantine `Checkbox` with a label, which is sized for a form |
|
|
883
|
+
| A checkbox that reveals the settings it turns on | `ToggleWithContent` | -- |
|
|
884
|
+
| A dropdown in a panel row | `PanelField` with `kind="select"` | -- |
|
|
885
|
+
| A dropdown with a default the reader can override | `StyleSelect` | a `Select` with a hand-made reset button |
|
|
886
|
+
| A value typed freely, with presets | `ComboInput` | `Select` (fixed choices) or `Autocomplete` (suggestions, no chevron) |
|
|
887
|
+
| Two to six drawable options in a panel row | Mantine `SegmentedControl` with a glyph and a `VisuallyHidden` word per segment, beside a `TrailingSlot` | `IconGroupRow`, removed in 0.9 |
|
|
888
|
+
| An icon button that stays pressed | `ToggleIconButton` | `ActionIcon`, which is an action, not an on/off state |
|
|
889
|
+
| An icon button that opens a pop-out | `PopoutButton`, inside `Popout.Trigger` | `AdvancedButton`, which is for a row's or a section's advanced settings and does not light up while a panel is open |
|
|
890
|
+
| A floating panel that can be dragged and nest | `Popout` | Mantine `Popover`: one-off, not draggable, no nesting |
|
|
891
|
+
| An explanation bubble | `InfoCircle` | a `Tooltip`, which closes when the pointer leaves |
|
|
892
|
+
| A color with its opacity | `CompactColorInput` | Mantine `ColorInput`, which has no opacity field and no Figma picker |
|
|
893
|
+
|
|
894
|
+
A lone boolean is not a row: put it in the trailing slot of the row it modifies, or make it one
|
|
895
|
+
segment of a `SegmentedControl`. `ToggleRowGroup` warns in development when given only one child,
|
|
896
|
+
for exactly this reason.
|
|
897
|
+
|
|
898
|
+
### Use these instead of Mantine's
|
|
899
|
+
|
|
900
|
+
Where this package has its own component for a job a stock Mantine component also does, the
|
|
901
|
+
theme leaves the Mantine one unstyled, so it renders exactly as Mantine documents it. Use these
|
|
902
|
+
instead to get the compact, Figma-matched look:
|
|
903
|
+
|
|
904
|
+
| Mantine component | Use instead |
|
|
905
|
+
| ----------------- | ---------------------------------------------------------------------------- |
|
|
906
|
+
| `ColorInput` | `CompactColorInput` |
|
|
907
|
+
| `ColorPicker` | `CompactColorInput`, or `ColorPickerPanel` for the picker on its own surface |
|
|
661
908
|
|
|
662
909
|
Every component puts its own name, in kebab case, on a root `data-testid` --
|
|
663
910
|
`advanced-button`, `metric-row`, `popout-panel` -- so a test can find any of
|
|
664
911
|
them the same way.
|
|
665
912
|
|
|
913
|
+
## Breaking changes in the Figma release
|
|
914
|
+
|
|
915
|
+
compact-mantine is 0.x, and 0.9.0 redraws every component to match the Figma editor. The full
|
|
916
|
+
list, with every changed value, is [CHANGELOG-figma.md](./CHANGELOG-figma.md); Storybook's
|
|
917
|
+
[Upgrading to 0.9](https://graphty.app/storybook/compact-mantine/?path=/docs/introduction-upgrading-to-0-9--docs)
|
|
918
|
+
page has the migrations.
|
|
919
|
+
|
|
920
|
+
**Removed exports and props (code that stops compiling):**
|
|
921
|
+
|
|
922
|
+
1. **`IconGroupRow`**, `IconGroupRowProps` and `IconGroupOption` are removed. Use the themed
|
|
923
|
+
Mantine `SegmentedControl` directly: a glyph and a `VisuallyHidden` word per segment, the row's
|
|
924
|
+
trailing control in a `TrailingSlot` (see [the worked example](#a-worked-example)).
|
|
925
|
+
2. **`DataRow`** keeps only `name`, `value`, `icon`, `selected`, `onClick`, `onFocus`, `onBlur`
|
|
926
|
+
and `trailing`. `role`, the `DataRowRole` type, `tabIndex`, `onDoubleClick` and
|
|
927
|
+
`onContextMenu` are removed; a selected row is always `aria-current`. A list the reader
|
|
928
|
+
selects, renames or reorders is a `Tree` or a `PageList`; a find result is a `ResultRow`.
|
|
929
|
+
|
|
930
|
+
**Behavior changes:**
|
|
931
|
+
|
|
932
|
+
3. **`GradientEditor`** edits stop colors in one picker under the gradient bar, which follows the
|
|
933
|
+
selected stop; stop rows no longer open a pop-out, so it no longer needs a `PopoutManager`.
|
|
934
|
+
4. **`Tree`**: with `onMove`, Alt+ArrowUp / Alt+ArrowDown move the focused item among its
|
|
935
|
+
siblings (additive).
|
|
936
|
+
5. **Motion and timing.** Overlays have no transitions, and a tooltip opens after 1000ms (it was
|
|
937
|
+
immediate). Wrap your app in `Tooltip.Group` for the warm hand-off.
|
|
938
|
+
6. **Pop-outs.** Only one root pop-out is open on the page at a time; `PopoutRegion` no longer
|
|
939
|
+
keeps one open per region. `POPOUT_GAP` 8 -> 0, `POPOUT_NESTED_GAP` 4 -> 0.
|
|
940
|
+
7. **Select and Autocomplete** have `role="combobox"`; `Select` defaults to the outlined trigger
|
|
941
|
+
with a dark list opening over it. `Tabs` default to `variant="pills"`.
|
|
942
|
+
8. **Escape in a color field** reverts the typed value and stops at the field.
|
|
943
|
+
|
|
944
|
+
**Values and looks:**
|
|
945
|
+
|
|
946
|
+
9. **Panel grid.** `PANEL_GRID.WIDTH` 280 -> 240, `FIELD` 108 -> 88, and most other members
|
|
947
|
+
change value. Code that hard-codes a 280px panel must follow `PANEL_GRID.WIDTH`.
|
|
948
|
+
10. **Theme scales.** `spacing.sm` 6 -> 8; `radius.sm` / `md` 4 / 6 -> 5, `lg` / `xl` 8 / 12 ->
|
|
949
|
+
13; `fontSizes.xs` 10 -> 9, `lg` 14 -> 15, `xl` 16 -> 24; `lineHeights` are px; `shadows` are
|
|
950
|
+
Figma's elevations.
|
|
951
|
+
11. **Colors.** `colors.dark` is a neutral ramp, and `primaryColor` is the new `brand` palette
|
|
952
|
+
(`color="blue"` now gets Mantine's blue). `PANEL_INK.SELECTED` / `ON_SELECTED` are Figma's
|
|
953
|
+
pale selected ground and brand glyph; every `PANEL_INK` value is a `--cm-*` token.
|
|
954
|
+
12. **Focus.** `focusRing` is `"never"` and the ring is the theme's own 1px ring.
|
|
955
|
+
13. **Typeface.** The body font is the bundled Inter at 11/16 weight 450; `<Text size="sm">` and
|
|
956
|
+
`size="xs"` render at 450 with Figma's letter-spacing.
|
|
957
|
+
14. **Rows and sections.** `ControlSection`'s header is 40 tall with its rule below; `DataRow` is
|
|
958
|
+
32 tall with an 11px name; `DataTable` rows and header are 40; `FieldRow` puts captions above
|
|
959
|
+
by default (`labelPosition="inline"` restores beside).
|
|
960
|
+
15. **Display and overlays.** `Badge` defaults to the outlined look, `Kbd` is a dark key cap in
|
|
961
|
+
both schemes, `Modal` has no backdrop by default, `Loader` at `sm` is 16px, and
|
|
962
|
+
`PopoutButton` defaults to `sm` and shows its open state as the ghost button's
|
|
963
|
+
`aria-expanded` look.
|
|
964
|
+
16. **Mantine's `ColorInput` and `ColorPicker` are no longer themed.** They render as plain
|
|
965
|
+
Mantine. Use `CompactColorInput` (see
|
|
966
|
+
[Use these instead of Mantine's](#use-these-instead-of-mantines)).
|
|
967
|
+
|
|
666
968
|
## Contributing
|
|
667
969
|
|
|
668
970
|
The package lives in the [graphty monorepo](https://github.com/graphty-org/graphty-monorepo)
|
|
@@ -679,8 +981,27 @@ npm run build-storybook # what CI builds
|
|
|
679
981
|
```
|
|
680
982
|
|
|
681
983
|
Storybook is the development environment: every component has a page, and a
|
|
682
|
-
change to a component shows up there without a rebuild.
|
|
683
|
-
|
|
984
|
+
change to a component shows up there without a rebuild.
|
|
985
|
+
|
|
986
|
+
Stories live under `stories/`, one folder per sidebar section: `introduction/` (MDX pages),
|
|
987
|
+
`foundations/`, `components/<group>/` (`panels`, `inputs`, `selection`, `color`, `actions`,
|
|
988
|
+
`overlays`, `lists`, `data`, `shell`, each with an `Overview.mdx`), `patterns/`, and
|
|
989
|
+
`mantine/<group>/` for the themed Mantine components. `stories/helpers/` holds the shared story
|
|
990
|
+
helpers. A component has exactly one story file, titled `Components/<Group>/<Name>`, laid out
|
|
991
|
+
like every other:
|
|
992
|
+
|
|
993
|
+
- The JSDoc above `const meta` is the page's description: one sentence on what the component is
|
|
994
|
+
for, then "When to use it" (naming its nearest alternative), "Usage" (a code snippet),
|
|
995
|
+
"Keyboard and accessibility" and "Measurements" (taken from `design/figma-spec.md`, which the
|
|
996
|
+
published page does not cite).
|
|
997
|
+
- `Default` is the first story and is driven by `args`, so the Controls table edits it.
|
|
998
|
+
- `States` is the second, with `parameters: BOTH_SCHEMES` from `stories/helpers/schemes.ts`, so
|
|
999
|
+
the page shows every state in light and dark side by side. Chromatic still captures every story
|
|
1000
|
+
once per scheme.
|
|
1001
|
+
- Feature stories follow, then `Keyboard`, `Translated` and `RightToLeft`. Every story has a
|
|
1002
|
+
one-sentence JSDoc comment, and a story whose job is to test behavior keeps a `play` function.
|
|
1003
|
+
|
|
1004
|
+
New components need a story, a test and complete prop documentation -- the prop comments are compiled
|
|
684
1005
|
into both the published type declarations and Storybook's tables, so they are
|
|
685
1006
|
read by people who will never see this repository.
|
|
686
1007
|
|
|
@@ -713,4 +1034,5 @@ Bugs and questions: <https://github.com/graphty-org/graphty-monorepo/issues>.
|
|
|
713
1034
|
|
|
714
1035
|
## License
|
|
715
1036
|
|
|
716
|
-
MIT
|
|
1037
|
+
MIT. The bundled Inter typeface is licensed under the SIL Open Font License 1.1
|
|
1038
|
+
(`dist/fonts/LICENSE-Inter.txt`).
|