@scattered-light/loomiere 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/bin/loomiere.mjs +24 -0
- package/build/build-tokens.mjs +93 -49
- package/exporters/README.md +1 -1
- package/exporters/ag-grid-settings.mjs +7 -7
- package/exporters/ag-grid.mjs +6 -6
- package/exporters/chakra.mjs +43 -43
- package/exporters/component-styles.mjs +4 -4
- package/exporters/design-system.mjs +17 -12
- package/exporters/figma.mjs +774 -68
- package/exporters/mui.mjs +40 -40
- package/exporters/shadcn.mjs +5 -13
- package/exporters/tailwind.mjs +14 -5
- package/figma/README.md +136 -0
- package/figma/build-plugin.mjs +25 -0
- package/figma/engine.js +730 -0
- package/figma/library/README.md +92 -0
- package/figma/library/check/known-differences.md +20 -0
- package/figma/library/check/mojave.json +246 -0
- package/figma/library/kit/base.js +89 -0
- package/figma/library/kit/components.js +95 -0
- package/figma/library/kit/docs.js +37 -0
- package/figma/library/kit/fingerprint.js +143 -0
- package/figma/library/pages/02-palettes.js +40 -0
- package/figma/library/pages/03-color.js +126 -0
- package/figma/library/pages/04-type.js +56 -0
- package/figma/library/pages/05-scale.js +58 -0
- package/figma/library/pages/06-size.js +111 -0
- package/figma/library/pages/07-elevation-focus-motion.js +108 -0
- package/figma/library/pages/08-themes.js +36 -0
- package/figma/library/pages/09-fields/01-input.js +55 -0
- package/figma/library/pages/09-fields/02-checks.js +82 -0
- package/figma/library/pages/09-fields/03-field.js +32 -0
- package/figma/library/pages/09-fields/04-more-fields.js +144 -0
- package/figma/library/pages/09-fields/05-labels.js +14 -0
- package/figma/library/pages/09-fields/06-doc.js +75 -0
- package/figma/library/pages/10-buttons/01-buttons.js +97 -0
- package/figma/library/pages/10-buttons/02-doc.js +55 -0
- package/figma/library/pages/11-popups/01-popups.js +166 -0
- package/figma/library/pages/11-popups/02-more-popups.js +90 -0
- package/figma/library/pages/11-popups/03-labels.js +14 -0
- package/figma/library/pages/11-popups/04-doc.js +89 -0
- package/figma/library/pages/12-overlays/01-overlays.js +135 -0
- package/figma/library/pages/12-overlays/02-labels.js +10 -0
- package/figma/library/pages/12-overlays/03-doc.js +80 -0
- package/figma/library/pages/13-disclosure/01-disclosure.js +205 -0
- package/figma/library/pages/13-disclosure/02-collapsible.js +39 -0
- package/figma/library/pages/13-disclosure/03-labels.js +16 -0
- package/figma/library/pages/13-disclosure/04-doc.js +91 -0
- package/figma/library/pages/14-display/01-display.js +165 -0
- package/figma/library/pages/14-display/02-more-display.js +55 -0
- package/figma/library/pages/14-display/03-labels.js +14 -0
- package/figma/library/pages/14-display/04-doc.js +96 -0
- package/figma/library/pages/15-cover.js +87 -0
- package/figma/library/run-order.json +21 -0
- package/figma/library/tools/compare.mjs +11 -0
- package/figma/library/tools/diff-snippet.js +15 -0
- package/figma/library/tools/kit-script.mjs +21 -0
- package/figma/library/tools/run.mjs +27 -0
- package/figma/library/tools/runs.mjs +34 -0
- package/figma/plugin/code.js +746 -0
- package/figma/plugin/manifest.json +14 -0
- package/figma/plugin/ui.html +237 -0
- package/figma/plugin-main.js +14 -0
- package/generator/brand.mjs +16 -14
- package/generator/brand.schema.json +60 -0
- package/generator/generate.mjs +2 -2
- package/generator/pairs.mjs +6 -1
- package/generator/roles.mjs +55 -15
- package/generator/vision.mjs +7 -2
- package/lib/config.mjs +2 -0
- package/lib/contrast.mjs +8 -3
- package/lib/figma.mjs +338 -0
- package/lib/studio.mjs +4 -0
- package/lib/tool-server.mjs +7 -4
- package/package.json +5 -1
- package/tokens/README.md +10 -5
- package/tokens/component/avatar.json +41 -6
- package/tokens/semantic/color.json +1 -1
- package/tokens/semantic/elevation.json +8 -43
- package/tokens/semantic/typography.json +61 -94
- package/tools/brand-builder/builder.js +33 -4
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Figma library pages
|
|
2
|
+
|
|
3
|
+
The scripts that draw the library's pages in a Figma file: the foundation pages (Palettes, Color,
|
|
4
|
+
Type, Scale, Size, Elevation, Themes), every component set with its labels, each page's doc
|
|
5
|
+
frame, and the Cover. They read the file's variables, text styles and effect styles by name, so
|
|
6
|
+
the same scripts build the Mojave file or any copy synced with another brand.
|
|
7
|
+
|
|
8
|
+
They used to live only in the Mojave file. Here they can rebuild a page in any copy, or the whole
|
|
9
|
+
library in a new file, and a check run proves the result matches Mojave.
|
|
10
|
+
|
|
11
|
+
## Before you start
|
|
12
|
+
|
|
13
|
+
The file needs the theme first: run a Figma sync (`loomiere figma push`, or "sync Figma" in a
|
|
14
|
+
chat; see [../README.md](../README.md)). The page scripts only draw. They never create variables or
|
|
15
|
+
styles, and they stop with "No variable …" if one is missing.
|
|
16
|
+
|
|
17
|
+
Scripts run through the Figma connector's `use_figma`, which takes up to 50,000 characters of
|
|
18
|
+
code and has no network access. So the shared code (the kit) is stored in the file itself, and
|
|
19
|
+
each page script loads it from there.
|
|
20
|
+
|
|
21
|
+
## Build or patch a file
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
# A new file, or after a kit change: store the kit, then every page in order.
|
|
25
|
+
node tools/runs.mjs --store
|
|
26
|
+
# Only some pages, in run order. Each script rebuilds its page's sets and doc frame.
|
|
27
|
+
node tools/runs.mjs --pages Fields,Themes
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`runs.mjs` packs [run-order.json](run-order.json) into as few scripts as fit under the limit and
|
|
31
|
+
writes them to `runs/` as `run-01.js`, `run-02.js`… Run them in the file in that order. Each
|
|
32
|
+
prints what it built; a script that fails partway can be run again, since every script removes
|
|
33
|
+
what it made last time before drawing.
|
|
34
|
+
|
|
35
|
+
The order matters because pages use each other's components: Fields needs Button for some of its
|
|
36
|
+
sets, Themes shows Field, Select, Checkbox and Switch, and the later pages use Button, Field and
|
|
37
|
+
Toggle. When a page changes, rebuild it and then the pages after it that use its sets.
|
|
38
|
+
|
|
39
|
+
### What a rebuild replaces
|
|
40
|
+
|
|
41
|
+
A rebuild deletes a component set and draws it again. Instances inside the library pages are
|
|
42
|
+
redrawn with it. Instances anywhere else in the file, such as screens in a project copy, still
|
|
43
|
+
point at the deleted set and stop updating. So in a copy that already has designs:
|
|
44
|
+
|
|
45
|
+
- Change colors, sizes, type and effects with a Figma sync. It updates variables and styles in
|
|
46
|
+
place, and every bound layer follows.
|
|
47
|
+
- Add a new component by running its page's scripts. Sets that already exist are rebuilt in the
|
|
48
|
+
same place, so run only on files where nothing outside the library uses them yet, or rebuild
|
|
49
|
+
just the new set's script.
|
|
50
|
+
|
|
51
|
+
## Check a run against Mojave
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
node tools/runs.mjs --check --pages Overlays # draws on a "✱ check / Overlays" page
|
|
55
|
+
node tools/run.mjs --units Overlays # a script that returns the page's fingerprints
|
|
56
|
+
node tools/compare.mjs Overlays '<fingerprints>' # compares with check/mojave.json
|
|
57
|
+
node tools/run.mjs --diff Overlays=set:Dialog,doc:Overlays/Dialog # the layers that differ
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A check run builds on pages named "✱ check / …" beside the real ones and never touches the real
|
|
61
|
+
pages. Components it needs from pages that have no check page come from the real ones. Delete
|
|
62
|
+
the check pages when done, and clear the prefix with `node tools/run.mjs --real` (a script that
|
|
63
|
+
only resets it).
|
|
64
|
+
|
|
65
|
+
Fingerprints are short hashes of what each set, wrapper and doc section looks like: sizes,
|
|
66
|
+
layout, variables by name, text styles, instance targets. They never use node IDs, so two files
|
|
67
|
+
built the same way match. [check/known-differences.md](check/known-differences.md) lists where
|
|
68
|
+
Mojave and the scripts differ on purpose.
|
|
69
|
+
|
|
70
|
+
## Files
|
|
71
|
+
|
|
72
|
+
| File | What it is |
|
|
73
|
+
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
74
|
+
| `kit/base.js` | Lookups for variables, styles and pages, and the small builders every script uses. Stored as `docHelpers`. |
|
|
75
|
+
| `kit/components.js` | base.js plus icons, focus rings, component helpers, the Components column and `labelSets`. Stored as `compHelpers`. |
|
|
76
|
+
| `kit/docs.js` | base.js plus stages, captions, usage columns and spec tables for doc frames. Stored as `docKit`. |
|
|
77
|
+
| `kit/fingerprint.js` | Fingerprints and layer diffs for checks. Stored as `fingerprint`. |
|
|
78
|
+
| `pages/` | One script per foundation page; a folder per component page, numbered in run order. |
|
|
79
|
+
| `run-order.json` | The order to run the page scripts in. |
|
|
80
|
+
| `tools/kit-script.mjs` | Writes the script that stores the kit (`--only docKit` for one part, `--hashes` to compare). |
|
|
81
|
+
| `tools/run.mjs` | Bundles scripts into one `use_figma` script; `--check`, `--real`, `--store`, `--units`, `--diff`. |
|
|
82
|
+
| `tools/runs.mjs` | Packs run-order.json into numbered scripts under the size limit. |
|
|
83
|
+
| `tools/compare.mjs`, `tools/diff-snippet.js` | Compare fingerprints with Mojave, and show the layers that differ. |
|
|
84
|
+
| `check/mojave.json` | Mojave's fingerprints, 23 October 2026. |
|
|
85
|
+
|
|
86
|
+
## Adding a component
|
|
87
|
+
|
|
88
|
+
Write its script in the page's folder, after the existing ones: build the variants with the kit's
|
|
89
|
+
`comp`, combine them, give the set a description, then `place(set)` so it joins the Components
|
|
90
|
+
column. Add it to the page's labels script and doc script, and to `run-order.json`. Run it with
|
|
91
|
+
`--check` first, look at it, then run it for real. Once it is in Mojave, refresh `check/mojave.json`
|
|
92
|
+
with a `--units` run of that page.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Known differences from Mojave
|
|
2
|
+
|
|
3
|
+
`check/mojave.json` holds the fingerprints of the Mojave file as it stood on 23 October 2026. A check
|
|
4
|
+
run of every page matches it, except for the differences below. In each case the scripts are right
|
|
5
|
+
and Mojave is out of date: either Mojave was built before a later change, or text was edited by
|
|
6
|
+
hand in Mojave and the scripts now carry the edit.
|
|
7
|
+
|
|
8
|
+
| Page | Where | Difference |
|
|
9
|
+
| ------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
10
|
+
| Color | Contrast tables | Mojave shows older ratios; the scripts compute them from the current variables when they run. |
|
|
11
|
+
| Color | Theme rows | Mojave's rows follow the old variable order; the scripts follow the current order. |
|
|
12
|
+
| Size | Opacity rows | Mojave shows opacity as 0.5; the scripts show 50, which is how Figma reads a bound opacity. |
|
|
13
|
+
| Fields | Picker rows 5 to 8, the Combobox "Avoid" line | Layer names. The text was edited in place in Mojave, so the layers kept their old names. |
|
|
14
|
+
| Buttons | Picker row 5 | Layer name, as above. |
|
|
15
|
+
| Popups | Picker rows 7 to 9 | Layer names, as above. |
|
|
16
|
+
| Cover | Lead text | Layer name, as above. |
|
|
17
|
+
| Cover | Title block | Mojave reports hug width while being 600 wide; the script fixes it at 600, since a hugging block would shrink to the title. |
|
|
18
|
+
|
|
19
|
+
When a check shows a difference that is not on this list, either a script or Mojave changed. Run
|
|
20
|
+
`node tools/run.mjs --diff Page=unit,unit` to see the layers that differ.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "4vd4kRXowo3AjoQNWsDu0D",
|
|
3
|
+
"taken": "2026-10-23",
|
|
4
|
+
"pages": {
|
|
5
|
+
"Cover": {
|
|
6
|
+
"frame:Cover": "12ef885a"
|
|
7
|
+
},
|
|
8
|
+
"Palettes": {
|
|
9
|
+
"doc:Palettes(frame)": "770c0cb8",
|
|
10
|
+
"doc:Palettes/Header": "aef6c9ee",
|
|
11
|
+
"doc:Palettes/Neutral": "935b1685",
|
|
12
|
+
"doc:Palettes/Primary": "bb6c309c",
|
|
13
|
+
"doc:Palettes/Secondary": "3caecc37",
|
|
14
|
+
"doc:Palettes/Tertiary": "0ff1f3f0",
|
|
15
|
+
"doc:Palettes/Quaternary": "f256b3b6",
|
|
16
|
+
"doc:Palettes/Info": "e19b2086",
|
|
17
|
+
"doc:Palettes/Success": "906f40a1",
|
|
18
|
+
"doc:Palettes/Warning": "a2b3b587",
|
|
19
|
+
"doc:Palettes/Error": "e558e4ef",
|
|
20
|
+
"doc:Palettes/Data colors": "18c701f7"
|
|
21
|
+
},
|
|
22
|
+
"Color": {
|
|
23
|
+
"doc:Color(frame)": "bc97eff3",
|
|
24
|
+
"doc:Color/Header": "06674d9c",
|
|
25
|
+
"doc:Color/How the layers fit": "255539db",
|
|
26
|
+
"doc:Color/Surfaces": "0cddb652",
|
|
27
|
+
"doc:Color/Text": "96bca119",
|
|
28
|
+
"doc:Color/Borders": "89f1f42d",
|
|
29
|
+
"doc:Color/Fills": "fc7b43a6",
|
|
30
|
+
"doc:Color/Interactive": "9284003b",
|
|
31
|
+
"doc:Color/Status": "fa79dc92",
|
|
32
|
+
"doc:Color/Focus and overlay": "07a1d16c",
|
|
33
|
+
"doc:Color/Long-form content": "dc067f0a",
|
|
34
|
+
"doc:Color/Status and emphasis": "5ab44147"
|
|
35
|
+
},
|
|
36
|
+
"Type": {
|
|
37
|
+
"doc:Type(frame)": "50dfb9ed",
|
|
38
|
+
"doc:Type/Header": "f00b32a5",
|
|
39
|
+
"doc:Type/Title": "06b09c65",
|
|
40
|
+
"doc:Type/Body": "1d34862d",
|
|
41
|
+
"doc:Type/Label": "11175252",
|
|
42
|
+
"doc:Type/Trigger": "14d64d28",
|
|
43
|
+
"doc:Type/Selected": "c79e22e5",
|
|
44
|
+
"doc:Type/Link": "8ff20a84",
|
|
45
|
+
"doc:Type/Subtitle": "a0a22231",
|
|
46
|
+
"doc:Type/Eyebrow": "03c9c6ae",
|
|
47
|
+
"doc:Type/Code": "5d3c6d39"
|
|
48
|
+
},
|
|
49
|
+
"Scale": {
|
|
50
|
+
"doc:Scale(frame)": "14751dc4",
|
|
51
|
+
"doc:Scale/Header": "f303ef41",
|
|
52
|
+
"doc:Scale/Space": "efb36c2c",
|
|
53
|
+
"doc:Scale/Radius": "5342775b",
|
|
54
|
+
"doc:Scale/Stroke": "9477db2c",
|
|
55
|
+
"doc:Scale/Font size": "78890681"
|
|
56
|
+
},
|
|
57
|
+
"Size": {
|
|
58
|
+
"doc:Size(frame)": "3da2a99c",
|
|
59
|
+
"doc:Size/Header": "7de475f6",
|
|
60
|
+
"doc:Size/Space": "a549b294",
|
|
61
|
+
"doc:Size/Radius": "22610155",
|
|
62
|
+
"doc:Size/Stroke": "d68d19fa",
|
|
63
|
+
"doc:Size/Controls and icons": "460221cb",
|
|
64
|
+
"doc:Size/Spec reference": "7f6192bc"
|
|
65
|
+
},
|
|
66
|
+
"Elevation, focus and motion": {
|
|
67
|
+
"doc:Elevation, focus and motion(frame)": "f3ae9a72",
|
|
68
|
+
"doc:Elevation, focus and motion/Header": "5a542c9e",
|
|
69
|
+
"doc:Elevation, focus and motion/Elevation": "c20ef33d",
|
|
70
|
+
"doc:Elevation, focus and motion/Focus": "59275e2f",
|
|
71
|
+
"doc:Elevation, focus and motion/Motion": "5371fc6a",
|
|
72
|
+
"doc:Elevation, focus and motion/Jobs": "d02b4be0"
|
|
73
|
+
},
|
|
74
|
+
"Themes": {
|
|
75
|
+
"doc:Themes(frame)": "8afb7fff",
|
|
76
|
+
"doc:Themes/Header": "6c4eb058",
|
|
77
|
+
"doc:Themes/All eight themes": "8a55f844"
|
|
78
|
+
},
|
|
79
|
+
"Fields": {
|
|
80
|
+
"set:Field": "f7cb48fb",
|
|
81
|
+
"set:Input": "6c2b0d54",
|
|
82
|
+
"set:Select": "442baeee",
|
|
83
|
+
"set:Textarea": "92f7f774",
|
|
84
|
+
"set:Checkbox": "2599731a",
|
|
85
|
+
"set:Radio": "91b6e532",
|
|
86
|
+
"set:Switch": "19bb0959",
|
|
87
|
+
"set:Combobox field": "01c1d61f",
|
|
88
|
+
"set:Autocomplete": "a81a3346",
|
|
89
|
+
"set:Number field": "58d13ece",
|
|
90
|
+
"set:OTP field": "f6a1224f",
|
|
91
|
+
"set:Checkbox group": "f5f67eac",
|
|
92
|
+
"set:Fieldset": "70450478",
|
|
93
|
+
"set:Form": "012115e1",
|
|
94
|
+
"frame:Components(shell)": "a43d23d9",
|
|
95
|
+
"doc:Fields(frame)": "1f58d1ef",
|
|
96
|
+
"doc:Fields/Header": "ac90f480",
|
|
97
|
+
"doc:Fields/Choosing a control": "60930777",
|
|
98
|
+
"doc:Fields/Field": "52eb7ea8",
|
|
99
|
+
"doc:Fields/Input, Select and Textarea": "667585cd",
|
|
100
|
+
"doc:Fields/Checkbox and Radio": "98e44af9",
|
|
101
|
+
"doc:Fields/Switch": "9f6d0241",
|
|
102
|
+
"doc:Fields/Combobox and Autocomplete": "39ad22a9",
|
|
103
|
+
"doc:Fields/Number field and OTP field": "3d859762",
|
|
104
|
+
"doc:Fields/Checkbox group": "2f95ef55",
|
|
105
|
+
"doc:Fields/Fieldset and Form": "ee563701",
|
|
106
|
+
"wrap:Field": "760b8799",
|
|
107
|
+
"wrap:Input": "d5883aac",
|
|
108
|
+
"wrap:Select": "94429931",
|
|
109
|
+
"wrap:Textarea": "88452dd3",
|
|
110
|
+
"wrap:Checkbox": "d0ade3f2",
|
|
111
|
+
"wrap:Radio": "97c80ea7",
|
|
112
|
+
"wrap:Switch": "740522a5",
|
|
113
|
+
"wrap:Combobox field": "46e241db",
|
|
114
|
+
"wrap:Autocomplete": "ed53691e",
|
|
115
|
+
"wrap:Number field": "738d8ca7",
|
|
116
|
+
"wrap:OTP field": "8476481b",
|
|
117
|
+
"wrap:Checkbox group": "04546bc2",
|
|
118
|
+
"wrap:Fieldset": "049ea0b8",
|
|
119
|
+
"wrap:Form": "80971363"
|
|
120
|
+
},
|
|
121
|
+
"Buttons": {
|
|
122
|
+
"set:Button": "59742be5",
|
|
123
|
+
"set:Toggle": "f85382f0",
|
|
124
|
+
"frame:Components(shell)": "b607a157",
|
|
125
|
+
"doc:Buttons(frame)": "fb3c0173",
|
|
126
|
+
"doc:Buttons/Header": "33e88cd7",
|
|
127
|
+
"doc:Buttons/Choosing a button": "10202b27",
|
|
128
|
+
"doc:Buttons/Emphasis and status": "ec54ae73",
|
|
129
|
+
"doc:Buttons/Sizes and icons": "75bcfc1c",
|
|
130
|
+
"doc:Buttons/Icon only": "05a78b7a",
|
|
131
|
+
"doc:Buttons/Toggle": "1b4b097e",
|
|
132
|
+
"wrap:Button": "6b3cb817",
|
|
133
|
+
"wrap:Toggle": "1c3b06ef"
|
|
134
|
+
},
|
|
135
|
+
"Popups": {
|
|
136
|
+
"set:Menu item": "f7b7f316",
|
|
137
|
+
"set:List item": "e0831f16",
|
|
138
|
+
"set:Popup part": "c5922528",
|
|
139
|
+
"set:Menu": "6f370851",
|
|
140
|
+
"set:List popup": "20c43337",
|
|
141
|
+
"set:Popover": "09663bde",
|
|
142
|
+
"set:Tooltip": "a649c419",
|
|
143
|
+
"set:Menubar trigger": "3b163892",
|
|
144
|
+
"set:Menubar": "b48c1f59",
|
|
145
|
+
"set:Context menu": "6036f0e7",
|
|
146
|
+
"set:Preview card": "c3aa3f00",
|
|
147
|
+
"frame:Components(shell)": "e98d9c96",
|
|
148
|
+
"doc:Popups(frame)": "63b5c8ea",
|
|
149
|
+
"doc:Popups/Header": "f33b648c",
|
|
150
|
+
"doc:Popups/Choosing a popup": "b5405324",
|
|
151
|
+
"doc:Popups/Menu": "03945f79",
|
|
152
|
+
"doc:Popups/Select and Combobox lists": "dd7345e6",
|
|
153
|
+
"doc:Popups/Popover": "deedd40c",
|
|
154
|
+
"doc:Popups/Tooltip": "3b34f8c2",
|
|
155
|
+
"doc:Popups/Context menu and Menubar": "88aa53f4",
|
|
156
|
+
"doc:Popups/Preview card": "d7b5638d",
|
|
157
|
+
"wrap:Menu item": "d5656be8",
|
|
158
|
+
"wrap:List item": "d6e17ed3",
|
|
159
|
+
"wrap:Popup part": "bfa16f2c",
|
|
160
|
+
"wrap:Menu": "4e21aec7",
|
|
161
|
+
"wrap:List popup": "df9d7af3",
|
|
162
|
+
"wrap:Popover": "6bcad5ee",
|
|
163
|
+
"wrap:Tooltip": "1871ff95",
|
|
164
|
+
"wrap:Menubar trigger": "c6bd01e6",
|
|
165
|
+
"wrap:Menubar": "38c11276",
|
|
166
|
+
"wrap:Context menu": "6aed485d",
|
|
167
|
+
"wrap:Preview card": "87d2bd4a"
|
|
168
|
+
},
|
|
169
|
+
"Overlays": {
|
|
170
|
+
"set:Dialog": "0561ff0b",
|
|
171
|
+
"set:Drawer": "5d4508a8",
|
|
172
|
+
"set:Toast": "f7e3afc8",
|
|
173
|
+
"frame:Components(shell)": "5ba0aa18",
|
|
174
|
+
"doc:Overlays(frame)": "52a37527",
|
|
175
|
+
"doc:Overlays/Header": "9779ec4e",
|
|
176
|
+
"doc:Overlays/Choosing an overlay": "953a3511",
|
|
177
|
+
"doc:Overlays/Dialog": "7c1d85f6",
|
|
178
|
+
"doc:Overlays/Drawer": "58eb79fb",
|
|
179
|
+
"doc:Overlays/Toast": "430c4eee",
|
|
180
|
+
"wrap:Dialog": "39d7f8c9",
|
|
181
|
+
"wrap:Drawer": "a7547195",
|
|
182
|
+
"wrap:Toast": "0dbfb765"
|
|
183
|
+
},
|
|
184
|
+
"Disclosure and navigation": {
|
|
185
|
+
"set:Tab": "9956f55e",
|
|
186
|
+
"set:Tab list": "65ecc9ee",
|
|
187
|
+
"set:Accordion item": "4bc88874",
|
|
188
|
+
"set:Accordion": "08a20c6c",
|
|
189
|
+
"set:Nav trigger": "6dfd79f5",
|
|
190
|
+
"set:Nav link": "95643ccc",
|
|
191
|
+
"set:Navigation menu": "5fe72c8b",
|
|
192
|
+
"set:Toolbar": "20cbc081",
|
|
193
|
+
"set:Toggle group": "7f7d2f59",
|
|
194
|
+
"set:Collapsible": "470bec8d",
|
|
195
|
+
"frame:Components(shell)": "b3fd0dbc",
|
|
196
|
+
"doc:Disclosure and navigation(frame)": "9a5954ea",
|
|
197
|
+
"doc:Disclosure and navigation/Header": "9b37279e",
|
|
198
|
+
"doc:Disclosure and navigation/Choosing": "466ca5a9",
|
|
199
|
+
"doc:Disclosure and navigation/Tabs": "5e99defe",
|
|
200
|
+
"doc:Disclosure and navigation/Accordion": "255fff5e",
|
|
201
|
+
"doc:Disclosure and navigation/Navigation menu": "26b02c57",
|
|
202
|
+
"doc:Disclosure and navigation/Toolbar and Toggle group": "6c530db2",
|
|
203
|
+
"doc:Disclosure and navigation/Collapsible": "b4718c92",
|
|
204
|
+
"wrap:Tab": "3882aa7d",
|
|
205
|
+
"wrap:Tab list": "25caa0a9",
|
|
206
|
+
"wrap:Accordion item": "2a40ca7f",
|
|
207
|
+
"wrap:Accordion": "13924902",
|
|
208
|
+
"wrap:Nav trigger": "d34f44b0",
|
|
209
|
+
"wrap:Nav link": "8270ab86",
|
|
210
|
+
"wrap:Navigation menu": "943b347c",
|
|
211
|
+
"wrap:Toolbar": "def74e8f",
|
|
212
|
+
"wrap:Toggle group": "0a9794a6",
|
|
213
|
+
"wrap:Collapsible": "5b849605"
|
|
214
|
+
},
|
|
215
|
+
"Display": {
|
|
216
|
+
"set:Avatar": "ecb75184",
|
|
217
|
+
"set:Avatar group": "4b3380bf",
|
|
218
|
+
"set:Meter": "91905363",
|
|
219
|
+
"set:Progress": "2bf6bde2",
|
|
220
|
+
"set:Slider": "d0a9f876",
|
|
221
|
+
"set:Table row": "65b9d02d",
|
|
222
|
+
"set:Table": "504ce16f",
|
|
223
|
+
"set:Separator": "4fc10c4f",
|
|
224
|
+
"set:Scroll area": "d975ef8a",
|
|
225
|
+
"frame:Components(shell)": "2d92208b",
|
|
226
|
+
"doc:Display(frame)": "8e3f9ff8",
|
|
227
|
+
"doc:Display/Header": "ac51b33b",
|
|
228
|
+
"doc:Display/Choosing": "9c898af9",
|
|
229
|
+
"doc:Display/Avatar": "e22c9ae4",
|
|
230
|
+
"doc:Display/Meter and Progress": "1ba30ce0",
|
|
231
|
+
"doc:Display/Slider": "41be582c",
|
|
232
|
+
"doc:Display/Table": "a0cb6855",
|
|
233
|
+
"doc:Display/Separator": "4ca97dbe",
|
|
234
|
+
"doc:Display/Scroll area": "64eeb8b1",
|
|
235
|
+
"wrap:Avatar": "41a8b826",
|
|
236
|
+
"wrap:Avatar group": "f4b9cf55",
|
|
237
|
+
"wrap:Meter": "81dd7d37",
|
|
238
|
+
"wrap:Progress": "a1e0c526",
|
|
239
|
+
"wrap:Slider": "9e3cc9bb",
|
|
240
|
+
"wrap:Table row": "53077168",
|
|
241
|
+
"wrap:Table": "c3b9a6c7",
|
|
242
|
+
"wrap:Separator": "4187560c",
|
|
243
|
+
"wrap:Scroll area": "d9c44942"
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// The library kit's base: lookups by name for variables, text styles and pages, and the small
|
|
2
|
+
// builders every page script uses (auto layout, text, fills bound to variables). Stored in the
|
|
3
|
+
// file as shared plugin data ('loomiere', 'docHelpers'); scripts load it with
|
|
4
|
+
// var L = await new AsyncFunction('figma', figma.root.getSharedPluginData('loomiere', 'docHelpers'))(figma);
|
|
5
|
+
// Pages are found by name. While a check runs, ('loomiere', 'pagePrefix') holds a prefix, so a
|
|
6
|
+
// script builds "<prefix>Buttons" instead of touching the real Buttons page.
|
|
7
|
+
var cols = await figma.variables.getLocalVariableCollectionsAsync(), allVars = await figma.variables.getLocalVariablesAsync();
|
|
8
|
+
var CN = {}, COL = {}, V = {}, TS = {};
|
|
9
|
+
cols.forEach(function (c) { CN[c.id] = c; COL[c.name] = c; });
|
|
10
|
+
allVars.forEach(function (x) { V[CN[x.variableCollectionId].name + ':' + x.name] = x; });
|
|
11
|
+
(await figma.getLocalTextStylesAsync()).forEach(function (s) { TS[s.name] = s; });
|
|
12
|
+
// Every font the text styles use, so any brand's fonts are ready before text is made.
|
|
13
|
+
var FONTS = {};
|
|
14
|
+
Object.keys(TS).forEach(function (k) { var f = TS[k].fontName; FONTS[f.family + '/' + f.style] = f; });
|
|
15
|
+
for (var fk in FONTS) await figma.loadFontAsync(FONTS[fk]);
|
|
16
|
+
var PREFIX = figma.root.getSharedPluginData('loomiere', 'pagePrefix') || '';
|
|
17
|
+
// The library's pages, in file order.
|
|
18
|
+
var PAGES = ['Cover', 'Palettes', 'Color', 'Type', 'Scale', 'Size', 'Elevation, focus and motion', 'Themes', 'Fields', 'Buttons', 'Popups', 'Overlays', 'Disclosure and navigation', 'Display'];
|
|
19
|
+
function v(k) { if (!V[k]) throw new Error('No variable ' + k); return V[k]; }
|
|
20
|
+
function paint(k) { return figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: { r: 0, g: 0, b: 0 } }, 'color', v(k)); }
|
|
21
|
+
function fill(n, k) { n.fills = k ? [paint(k)] : []; return n; }
|
|
22
|
+
function stroke(n, k, w, align) { n.strokes = [paint(k)]; n.strokeAlign = align || 'INSIDE'; if (typeof w === 'string') n.setBoundVariable('strokeWeight', v(w)); else n.strokeWeight = w || 1; return n; }
|
|
23
|
+
function bind(n, fields, k) { (Array.isArray(fields) ? fields : [fields]).forEach(function (f) { n.setBoundVariable(f, v(k)); }); return n; }
|
|
24
|
+
function radius(n, k) { return bind(n, ['topLeftRadius', 'topRightRadius', 'bottomLeftRadius', 'bottomRightRadius'], k); }
|
|
25
|
+
function pad(n, block, inline) { if (block) bind(n, ['paddingTop', 'paddingBottom'], block); if (inline) bind(n, ['paddingLeft', 'paddingRight'], inline); return n; }
|
|
26
|
+
function AL(dir, props) { var f = figma.createAutoLayout(dir || 'HORIZONTAL'); f.fills = []; f.clipsContent = false; if (props) f.set(props); return f; }
|
|
27
|
+
function add(parent, kids) { kids.forEach(function (k) { if (k) parent.appendChild(k); }); return parent; }
|
|
28
|
+
async function T(str, style, colorKey, w, name) {
|
|
29
|
+
var t = figma.createText();
|
|
30
|
+
if (!TS[style]) throw new Error('No text style ' + style);
|
|
31
|
+
await t.setTextStyleIdAsync(TS[style].id);
|
|
32
|
+
t.characters = str;
|
|
33
|
+
fill(t, colorKey || 'Theme:text/primary');
|
|
34
|
+
if (w) { t.resize(w, t.height); t.textAutoResize = 'HEIGHT'; }
|
|
35
|
+
t.name = name || str.slice(0, 40);
|
|
36
|
+
return t;
|
|
37
|
+
}
|
|
38
|
+
function unescape(s) { return (s || '').replace(/&(quot|#39|lt|gt|amp);/g, function (m, e) { return { quot: '"', '#39': "'", lt: '<', gt: '>', amp: '&' }[e]; }); }
|
|
39
|
+
function mode(n, colName, modeName) { var c = COL[colName]; n.setExplicitVariableModeForCollection(c, c.modes.filter(function (m) { return m.name === modeName; })[0].modeId); return n; }
|
|
40
|
+
// A library page by its name (with the check prefix while a check runs), or null.
|
|
41
|
+
function findPage(name) { return figma.root.children.filter(function (x) { return x.name === PREFIX + name; })[0] || null; }
|
|
42
|
+
// Switch to a library page, making it in its place in PAGES if the file doesn't have it.
|
|
43
|
+
async function page(name) {
|
|
44
|
+
var p = findPage(name);
|
|
45
|
+
if (!p) {
|
|
46
|
+
p = figma.createPage(); p.name = PREFIX + name;
|
|
47
|
+
var at = PAGES.indexOf(name), next = null;
|
|
48
|
+
for (var i = at + 1; at !== -1 && i < PAGES.length && !next; i++) next = findPage(PAGES[i]);
|
|
49
|
+
if (next) figma.root.insertChild(figma.root.children.indexOf(next), p);
|
|
50
|
+
}
|
|
51
|
+
await figma.setCurrentPageAsync(p);
|
|
52
|
+
return p;
|
|
53
|
+
}
|
|
54
|
+
// A component set by name on another library page (loads that page).
|
|
55
|
+
async function setOn(pageName, setName) {
|
|
56
|
+
// A check run falls back to the real page for sets it hasn't rebuilt yet.
|
|
57
|
+
var p = findPage(pageName) || (PREFIX && figma.root.children.filter(function (x) { return x.name === pageName; })[0]);
|
|
58
|
+
if (!p) throw new Error('No page ' + pageName);
|
|
59
|
+
await p.loadAsync();
|
|
60
|
+
var s = p.findAllWithCriteria({ types: ['COMPONENT_SET', 'COMPONENT'] }).filter(function (n) { return n.name === setName && (n.type === 'COMPONENT_SET' || n.parent.type !== 'COMPONENT_SET'); })[0];
|
|
61
|
+
if (!s) throw new Error('No component ' + setName + ' on ' + pageName);
|
|
62
|
+
return s;
|
|
63
|
+
}
|
|
64
|
+
async function docFrame(name, eyebrow, title, lead) {
|
|
65
|
+
var old = figma.currentPage.children.filter(function (x) { return x.name === name; });
|
|
66
|
+
old.forEach(function (x) { x.remove(); });
|
|
67
|
+
var f = AL('VERTICAL', { name: name, itemSpacing: 72, paddingTop: 96, paddingBottom: 120, paddingLeft: 96, paddingRight: 96 });
|
|
68
|
+
fill(f, 'Theme:surface/canvas');
|
|
69
|
+
f.resize(1440, 100); f.counterAxisSizingMode = 'FIXED'; f.primaryAxisSizingMode = 'AUTO';
|
|
70
|
+
var head = AL('VERTICAL', { name: 'Header', itemSpacing: 16 });
|
|
71
|
+
add(head, [await T(eyebrow, 'eyebrow-condense', 'Theme:text/accent'), await T(title, 'title-maximize'), await T(lead, 'body-magnify', 'Theme:text/secondary', 820)]);
|
|
72
|
+
add(f, [head]);
|
|
73
|
+
return f;
|
|
74
|
+
}
|
|
75
|
+
async function section(parent, title, intro) {
|
|
76
|
+
var s = AL('VERTICAL', { name: title, itemSpacing: 24 });
|
|
77
|
+
add(parent, [s]); s.layoutSizingHorizontal = 'FILL';
|
|
78
|
+
var h = AL('VERTICAL', { name: 'Section header', itemSpacing: 8 });
|
|
79
|
+
add(h, [await T(title, 'title-amplify'), intro ? await T(intro, 'body-base', 'Theme:text/secondary', 820) : null]);
|
|
80
|
+
add(s, [h]);
|
|
81
|
+
return s;
|
|
82
|
+
}
|
|
83
|
+
function hrow(parent) { var r = AL('HORIZONTAL', { itemSpacing: 24, counterAxisAlignItems: 'CENTER', paddingTop: 12, paddingBottom: 12 }); add(parent, [r]); r.layoutSizingHorizontal = 'FILL'; return r; }
|
|
84
|
+
function divider(n) { n.strokes = [paint('Theme:border/subtle')]; n.strokeTopWeight = 0; n.strokeLeftWeight = 0; n.strokeRightWeight = 0; n.strokeBottomWeight = 1; return n; }
|
|
85
|
+
function lum(c) { var f = function (x) { return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); }; return 0.2126 * f(c.r) + 0.7152 * f(c.g) + 0.0722 * f(c.b); }
|
|
86
|
+
function ratio(a, b) { var x = lum(a), y = lum(b); return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05); }
|
|
87
|
+
function resolved(k, node) { var r = v(k).resolveForConsumer(node); return r.value; }
|
|
88
|
+
function hex(c) { var p = function (x) { return ('0' + Math.round(x * 255).toString(16)).slice(-2); }; return '#' + p(c.r) + p(c.g) + p(c.b); }
|
|
89
|
+
return { V: V, COL: COL, TS: TS, PAGES: PAGES, PREFIX: PREFIX, v: v, paint: paint, fill: fill, stroke: stroke, bind: bind, radius: radius, pad: pad, AL: AL, add: add, T: T, unescape: unescape, mode: mode, findPage: findPage, page: page, setOn: setOn, docFrame: docFrame, section: section, hrow: hrow, divider: divider, lum: lum, ratio: ratio, resolved: resolved, hex: hex };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// The kit for component scripts: everything in base.js, plus icons, focus rings, component and
|
|
2
|
+
// variant helpers, and the page's Components column. Stored as ('loomiere', 'compHelpers').
|
|
3
|
+
var L = await new (Object.getPrototypeOf(async function () {}).constructor)('figma', figma.root.getSharedPluginData('loomiere', 'docHelpers'))(figma);
|
|
4
|
+
var V = L.V, COL = L.COL, TS = L.TS, v = L.v, paint = L.paint, fill = L.fill, stroke = L.stroke, bind = L.bind, radius = L.radius, pad = L.pad, AL = L.AL, add = L.add, T = L.T, mode = L.mode, page = L.page;
|
|
5
|
+
function shadow(color, spread, opts) {
|
|
6
|
+
var e = { type: 'DROP_SHADOW', color: { r: 0, g: 0, b: 0, a: 1 }, offset: { x: 0, y: (opts && opts.y) || 0 }, radius: (opts && opts.blur) || 0, spread: spread, visible: true, blendMode: 'NORMAL', showShadowBehindNode: false };
|
|
7
|
+
return color ? figma.variables.setBoundVariableForEffect(e, 'color', v(color)) : Object.assign(e, { color: opts.rgba });
|
|
8
|
+
}
|
|
9
|
+
function focusRing() { return [shadow('Theme:surface/canvas', 2), shadow('Theme:focus/ring', 4), shadow('Theme:interactive/tint-hover', 6)]; }
|
|
10
|
+
function icon(name, d, size, colorKey, weight) {
|
|
11
|
+
var f = figma.createFrame(); f.name = name; f.fills = []; f.resize(16, 16); f.clipsContent = false;
|
|
12
|
+
var vec = figma.createVector(); vec.vectorPaths = [{ windingRule: 'NONZERO', data: d }]; vec.fills = [];
|
|
13
|
+
vec.strokes = [paint(colorKey)]; vec.strokeWeight = weight || 1.5; vec.strokeCap = 'ROUND'; vec.strokeJoin = 'ROUND';
|
|
14
|
+
var xs = [], ys = []; d.replace(/(-?[\d.]+) (-?[\d.]+)/g, function (m, x, y) { xs.push(+x); ys.push(+y); });
|
|
15
|
+
f.appendChild(vec); vec.x = Math.min.apply(null, xs); vec.y = Math.min.apply(null, ys);
|
|
16
|
+
if (size !== 16) f.rescale(size / 16);
|
|
17
|
+
return f;
|
|
18
|
+
}
|
|
19
|
+
function comp(name, dir) { var c = figma.createComponent(); c.name = name; c.layoutMode = dir || 'HORIZONTAL'; c.primaryAxisSizingMode = 'AUTO'; c.counterAxisSizingMode = 'AUTO'; c.fills = []; return c; }
|
|
20
|
+
function arrange(set, width) {
|
|
21
|
+
set.layoutMode = 'HORIZONTAL'; set.layoutWrap = 'WRAP'; set.itemSpacing = 24; set.counterAxisSpacing = 24;
|
|
22
|
+
set.paddingTop = set.paddingBottom = set.paddingLeft = set.paddingRight = 32;
|
|
23
|
+
set.primaryAxisSizingMode = 'FIXED'; set.counterAxisSizingMode = 'AUTO'; set.resize(width, set.height);
|
|
24
|
+
fill(set, 'Theme:surface/canvas'); set.strokes = [{ type: 'SOLID', color: { r: 0.592, g: 0.278, b: 1 } }]; set.dashPattern = [6, 4]; set.strokeWeight = 1; set.cornerRadius = 8;
|
|
25
|
+
}
|
|
26
|
+
function textProp(set, label, def, layerName) {
|
|
27
|
+
var key = set.addComponentProperty(label, 'TEXT', def);
|
|
28
|
+
set.findAllWithCriteria({ types: ['TEXT'] }).forEach(function (t) { if (t.name === layerName) t.componentPropertyReferences = Object.assign({}, t.componentPropertyReferences, { characters: key }); });
|
|
29
|
+
return key;
|
|
30
|
+
}
|
|
31
|
+
function boolProp(set, label, def, layerName) {
|
|
32
|
+
var key = set.addComponentProperty(label, 'BOOLEAN', def);
|
|
33
|
+
set.findAll(function (n) { return n.name === layerName; }).forEach(function (n) { n.componentPropertyReferences = Object.assign({}, n.componentPropertyReferences, { visible: key }); });
|
|
34
|
+
return key;
|
|
35
|
+
}
|
|
36
|
+
// Where a rebuilt set goes back: removeOld notes the slot of the set (or its labelled wrapper) it
|
|
37
|
+
// removes, so place puts the new one in the same spot instead of at the end.
|
|
38
|
+
var slot = null;
|
|
39
|
+
function place(set) {
|
|
40
|
+
var col = figma.currentPage.children.filter(function (n) { return n.name === 'Components'; })[0];
|
|
41
|
+
if (!col) { col = AL('VERTICAL', { name: 'Components', itemSpacing: 80 }); col.x = 1600; col.y = 400; }
|
|
42
|
+
if (slot && slot.name === set.name && slot.index <= col.children.length) col.insertChild(slot.index, set); else col.appendChild(set);
|
|
43
|
+
slot = null;
|
|
44
|
+
return col;
|
|
45
|
+
}
|
|
46
|
+
function isWrap(n) { return n.type === 'FRAME' && n.getSharedPluginData('loomiere', 'wrap') === '1'; }
|
|
47
|
+
function removeOld(name) {
|
|
48
|
+
figma.currentPage.findAll(function (n) { return n.name === name && (n.type === 'COMPONENT_SET' || isWrap(n)); }).forEach(function (n) {
|
|
49
|
+
if (n.removed) return;
|
|
50
|
+
if (n.parent && n.parent.name === 'Components') slot = { name: name, index: n.parent.children.indexOf(n) };
|
|
51
|
+
n.remove();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// A header and side labels for each bare set in the page's Components column: the set's name and
|
|
55
|
+
// a line from desc (or its description), its column values above and row values to the left.
|
|
56
|
+
// Sets already wrapped keep their labels; run it last on a page, after every set is built.
|
|
57
|
+
async function labelSets(desc) {
|
|
58
|
+
desc = desc || {};
|
|
59
|
+
var comp = figma.currentPage.children.filter(function (n) { return n.name === 'Components'; })[0];
|
|
60
|
+
function bucket(vals, tol) { var out = []; vals.sort(function (a, b) { return a - b; }).forEach(function (v) { if (!out.length || v - out[out.length - 1][0] > tol) out.push([v]); else out[out.length - 1].push(v); }); return out.map(function (b) { return b[0]; }); }
|
|
61
|
+
function common(nodes) { var keys = Object.keys(nodes[0].variantProperties); return keys.filter(function (k) { return nodes.every(function (n) { return n.variantProperties[k] === nodes[0].variantProperties[k]; }); }).map(function (k) { return nodes[0].variantProperties[k]; }); }
|
|
62
|
+
var sets = comp.children.filter(function (c) { return c.type === 'COMPONENT_SET'; });
|
|
63
|
+
comp.itemSpacing = 120;
|
|
64
|
+
var LW = 120;
|
|
65
|
+
for (var i = 0; i < sets.length; i++) {
|
|
66
|
+
var s = sets[i], kids = s.children;
|
|
67
|
+
var w = AL('VERTICAL', { name: s.name, itemSpacing: 16 }); w.setSharedPluginData('loomiere', 'wrap', '1');
|
|
68
|
+
comp.insertChild(comp.children.indexOf(s), w);
|
|
69
|
+
var head = AL('VERTICAL', { name: 'Header', itemSpacing: 4 });
|
|
70
|
+
add(head, [await T(s.name, 'title-amplify'), await T(desc[s.name] || s.description || '', 'body-base', 'Theme:text/secondary', 720)]);
|
|
71
|
+
add(w, [head]);
|
|
72
|
+
var xs = bucket(kids.map(function (k) { return Math.round(k.x); }), 8), ys = bucket(kids.map(function (k) { return Math.round(k.y); }), 12);
|
|
73
|
+
var top = AL('HORIZONTAL', { name: 'Column labels row' });
|
|
74
|
+
var spacer = figma.createFrame(); spacer.name = 'Corner'; spacer.fills = []; spacer.resize(LW, 20);
|
|
75
|
+
var cl = figma.createFrame(); cl.name = 'Column labels'; cl.fills = []; cl.clipsContent = false; cl.resize(s.width, 20);
|
|
76
|
+
add(top, [spacer, cl]); add(w, [top]);
|
|
77
|
+
if (xs.length > 1) for (var c = 0; c < xs.length; c++) {
|
|
78
|
+
var col = kids.filter(function (k) { return Math.abs(Math.round(k.x) - xs[c]) <= 8; });
|
|
79
|
+
var t = await T(common(col).join(' · ').toUpperCase(), 'eyebrow-condense', 'Theme:text/tertiary'); cl.appendChild(t); t.x = xs[c]; t.y = 0;
|
|
80
|
+
}
|
|
81
|
+
var body = AL('HORIZONTAL', { name: 'Body' });
|
|
82
|
+
var rl = figma.createFrame(); rl.name = 'Row labels'; rl.fills = []; rl.clipsContent = false; rl.resize(LW, s.height);
|
|
83
|
+
add(body, [rl]); body.appendChild(s); add(w, [body]);
|
|
84
|
+
if (ys.length > 1) for (var r = 0; r < ys.length; r++) {
|
|
85
|
+
var row = kids.filter(function (k) { return Math.abs(Math.round(k.y) - ys[r]) <= 12; });
|
|
86
|
+
var mid = row.reduce(function (a, k) { return a + k.y + k.height / 2; }, 0) / row.length;
|
|
87
|
+
var t2 = await T(common(row).join(' · '), 'subtitle-condense', 'Theme:text/secondary', LW - 16); rl.appendChild(t2); t2.x = 0; t2.y = Math.round(mid - t2.height / 2);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
comp.clipsContent = false;
|
|
91
|
+
comp.findAll(function (n) { return n.type === 'FRAME' && (isWrap(n) || ['Header', 'Column labels row', 'Body', 'Corner'].indexOf(n.name) !== -1); }).forEach(function (n) { n.clipsContent = false; });
|
|
92
|
+
return sets.map(function (s) { return s.name; });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return Object.assign({}, L, { shadow: shadow, focusRing: focusRing, icon: icon, comp: comp, arrange: arrange, textProp: textProp, boolProp: boolProp, place: place, removeOld: removeOld, labelSets: labelSets });
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// The kit for doc pages: everything in base.js, plus instances of the page's component sets,
|
|
2
|
+
// spec tables, example stages, captions and do/don't columns. Stored as ('loomiere', 'docKit').
|
|
3
|
+
var L = await new (Object.getPrototypeOf(async function () {}).constructor)('figma', figma.root.getSharedPluginData('loomiere', 'docHelpers'))(figma);
|
|
4
|
+
var fill = L.fill, paint = L.paint, AL = L.AL, add = L.add, T = L.T, hrow = L.hrow, divider = L.divider;
|
|
5
|
+
var sets = {}; figma.currentPage.findAllWithCriteria({ types: ['COMPONENT_SET'] }).forEach(function (s) { sets[s.name] = s; });
|
|
6
|
+
function inst(set, name, props) {
|
|
7
|
+
var c = sets[set].children.filter(function (x) { return x.name === name; })[0]; if (!c) throw new Error(set + ' ' + name);
|
|
8
|
+
var i = c.createInstance();
|
|
9
|
+
if (props) { var p = {}; Object.keys(i.componentProperties).forEach(function (k) { var base = k.split('#')[0]; if (props[base] !== undefined) p[k] = props[base]; }); i.setProperties(p); }
|
|
10
|
+
return i;
|
|
11
|
+
}
|
|
12
|
+
async function notes(parent, rows) {
|
|
13
|
+
var tbl = AL('VERTICAL', { name: 'Spec' }); add(parent, [tbl]); tbl.layoutSizingHorizontal = 'FILL';
|
|
14
|
+
var hd = hrow(tbl); hd.paddingTop = 0; divider(hd);
|
|
15
|
+
add(hd, [await T('Part', 'eyebrow-condense', 'Theme:text/tertiary', 180), await T('Token', 'eyebrow-condense', 'Theme:text/tertiary', 340), await T('Why', 'eyebrow-condense', 'Theme:text/tertiary', 640)]);
|
|
16
|
+
for (var i = 0; i < rows.length; i++) {
|
|
17
|
+
var r = hrow(tbl); r.counterAxisAlignItems = 'MIN'; r.paddingTop = r.paddingBottom = 10; divider(r);
|
|
18
|
+
add(r, [await T(rows[i][0], 'subtitle-base', 'Theme:text/primary', 180), await T(rows[i][1], 'code-condense', 'Theme:text/accent', 340), await T(rows[i][2] || ' ', 'body-condense', 'Theme:text/secondary', 640)]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function stage(parent, name) {
|
|
22
|
+
var s = AL('HORIZONTAL', { name: name || 'Examples', itemSpacing: 32, counterAxisAlignItems: 'MIN', paddingTop: 32, paddingBottom: 32, paddingLeft: 32, paddingRight: 32, layoutWrap: 'WRAP', counterAxisSpacing: 24 });
|
|
23
|
+
fill(s, 'Theme:fill/subtle'); s.cornerRadius = 12; add(parent, [s]); s.layoutSizingHorizontal = 'FILL'; return s;
|
|
24
|
+
}
|
|
25
|
+
async function captioned(node, cap) { var c = AL('VERTICAL', { name: cap, itemSpacing: 10 }); add(c, [node, await T(cap, 'body-condense', 'Theme:text/tertiary')]); return c; }
|
|
26
|
+
async function usage(parent, dos, donts) {
|
|
27
|
+
var r = AL('HORIZONTAL', { name: 'Usage', itemSpacing: 24 }); add(parent, [r]); r.layoutSizingHorizontal = 'FILL';
|
|
28
|
+
var pairs = [['Use it for', dos, 'Theme:status/success'], ['Avoid', donts, 'Theme:status/error']];
|
|
29
|
+
for (var i = 0; i < 2; i++) {
|
|
30
|
+
var col = AL('VERTICAL', { name: pairs[i][0], itemSpacing: 8, paddingLeft: 16 }); add(r, [col]); col.layoutSizingHorizontal = 'FILL';
|
|
31
|
+
col.strokes = [paint(pairs[i][2])]; col.strokeLeftWeight = 3; col.strokeTopWeight = col.strokeRightWeight = col.strokeBottomWeight = 0;
|
|
32
|
+
add(col, [await T(pairs[i][0], 'subtitle-base')]);
|
|
33
|
+
for (var j = 0; j < pairs[i][1].length; j++) { var t = await T(pairs[i][1][j], 'body-base', 'Theme:text/secondary'); add(col, [t]); t.layoutSizingHorizontal = 'FILL'; t.textAutoResize = 'HEIGHT'; }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function overflow(doc) { var over = []; doc.findAll(function (n) { return n.type === 'TEXT'; }).forEach(function (t) { var b = t.absoluteBoundingBox, p = doc.absoluteBoundingBox; if (b.x + b.width > p.x + p.width + 1) over.push(t.characters.slice(0, 30)); }); return over; }
|
|
37
|
+
return Object.assign({}, L, { sets: sets, inst: inst, notes: notes, stage: stage, captioned: captioned, usage: usage, overflow: overflow });
|