@rogieking/figui3 2.31.2 → 2.33.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.
@@ -143,7 +143,7 @@ export default defineConfig({
143
143
  ### Documentation and Demos
144
144
 
145
145
  - Update `README.md` component docs when public API or behavior changes.
146
- - Update demo pages (`index.html` and `propkit.html` where relevant) for visible behavior changes.
146
+ - Update demo surfaces (`index.html` and `playground/` routes where relevant) for visible behavior changes.
147
147
  - Prefer realistic examples that mirror plugin/property panel usage.
148
148
  - If introducing an experimental feature, document activation and fallback behavior clearly.
149
149
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: propkit
3
- description: Guides creation and refinement of Figma-style property panel patterns ("PropKit") using FigUI3 components. Applies when building or modifying property fields in `propkit.html`, generating consistent field prompts, composing horizontal `fig-field` rows, or tuning panel UX for controls like image, color, fill, slider, switch, dropdown, segmented control, easing, and angle.
3
+ description: Guides creation and refinement of Figma-style property panel patterns ("PropKit") using FigUI3 components. Applies when building or modifying property fields in the playground app (`/propkit` route), generating consistent field prompts, composing horizontal `fig-field` rows, or tuning panel UX for controls like image, color, fill, slider, switch, dropdown, segmented control, easing, and angle.
4
4
  user-invocable: false
5
5
  ---
6
6
 
@@ -13,7 +13,7 @@ Patterns for composing clean, production-ready Figma property panels with FigUI3
13
13
  ## Current Project Context
14
14
 
15
15
  ```json
16
- !`node -e "const fs=require('fs'); const ok=fs.existsSync('propkit.html'); console.log(JSON.stringify({propkit:ok, example:'horizontal fig-field + label + fig-* control'},null,2))" 2>/dev/null || echo '{"error":"context unavailable"}'`
16
+ !`node -e "const fs=require('fs'); const ok=fs.existsSync('playground/src/main.tsx'); console.log(JSON.stringify({playground:ok, route:'/propkit', example:'horizontal fig-field + label + fig-* control'},null,2))" 2>/dev/null || echo '{"error":"context unavailable"}'`
17
17
  ```
18
18
 
19
19
  ## Principles
@@ -199,7 +199,7 @@ Use a horizontal fig-field, with a fig-slider, min=0 max=100 text=true units=%.
199
199
  - Confirm control choice matches intent (continuous vs discrete vs boolean vs exact numeric entry).
200
200
  - Verify row density and panel width feel consistent with existing PropKit sections.
201
201
  - Verify keyboard navigation and label association for every field row.
202
- - Verify changes in `propkit.html` still mirror recommended patterns in this skill.
202
+ - Verify changes in `playground/src/data/sections.ts` still mirror recommended patterns in this skill.
203
203
 
204
204
  ## Quick Reference
205
205
 
@@ -218,7 +218,7 @@ Common PropKit controls:
218
218
 
219
219
  ## Primary Files
220
220
 
221
- - `propkit.html` - canonical PropKit examples and prompt-copy behavior
221
+ - `playground/src/data/sections.ts` - canonical PropKit examples and prompt-copy behavior
222
222
  - `fig.js` - control behavior and emitted events
223
223
  - `components.css` - visual treatment and layout constraints
224
224
  - `README.md` - component API details and usage
package/README.md CHANGED
@@ -62,7 +62,8 @@ Or via esm.sh:
62
62
  git clone https://github.com/rogie/figui3.git
63
63
  cd figui3
64
64
  bun install
65
- bun dev # Opens documentation at http://localhost:3000
65
+ bun dev # Core component docs at http://localhost:3000
66
+ npm run dev:playground # Interactive playground app (routes: /figui3, /propkit)
66
67
  ```
67
68
 
68
69
  ## Quick Start
package/components.css CHANGED
@@ -1212,8 +1212,6 @@ fig-chit {
1212
1212
  background-repeat: no-repeat;
1213
1213
  border-radius: 0.125rem;
1214
1214
  box-shadow: inset 0 0 0 1px var(--figma-color-bordertranslucent);
1215
- }
1216
- &[data-type="image"]::after {
1217
1215
  mask-image: linear-gradient(
1218
1216
  to right,
1219
1217
  black 0%,
@@ -2355,6 +2353,7 @@ dialog[is="fig-dialog"] {
2355
2353
 
2356
2354
  dialog[is="fig-popup"] {
2357
2355
  --z-index: 999999;
2356
+ --beak-offset: 1px;
2358
2357
  z-index: var(--z-index);
2359
2358
  position: fixed;
2360
2359
  margin: 0;
@@ -2419,13 +2418,13 @@ dialog[is="fig-popup"] {
2419
2418
  }
2420
2419
 
2421
2420
  &[data-beak-side="left"]:after {
2422
- left: 1px;
2421
+ left: 6px;
2423
2422
  top: var(--beak-offset, 50%);
2424
2423
  transform: translate(-100%, -50%) rotate(90deg);
2425
2424
  }
2426
2425
 
2427
2426
  &[data-beak-side="right"]:after {
2428
- left: calc(100% - 1px);
2427
+ left: calc(100% - 6px);
2429
2428
  top: var(--beak-offset, 50%);
2430
2429
  transform: translate(0, -50%) rotate(-90deg);
2431
2430
  }
@@ -2894,11 +2893,36 @@ fig-field,
2894
2893
  gap: var(--spacer-2);
2895
2894
  align-items: start;
2896
2895
  flex-direction: row;
2897
- width: auto;
2898
2896
 
2899
2897
  & > label {
2900
2898
  min-width: 4rem;
2901
2899
  }
2900
+ &[columns="thirds"] {
2901
+ display: grid;
2902
+ grid-template-columns: repeat(3, 1fr);
2903
+ gap: var(--spacer-2);
2904
+
2905
+ & > label {
2906
+ grid-column: 1;
2907
+ }
2908
+
2909
+ & > label ~ * {
2910
+ grid-column: 2 / span 2;
2911
+ }
2912
+ }
2913
+ &[columns="half"] {
2914
+ display: grid;
2915
+ grid-template-columns: repeat(2, 1fr);
2916
+ gap: var(--spacer-2);
2917
+
2918
+ & > label {
2919
+ grid-column: 1;
2920
+ }
2921
+
2922
+ & > label ~ * {
2923
+ grid-column: 2 / span 2;
2924
+ }
2925
+ }
2902
2926
  }
2903
2927
  }
2904
2928
 
@@ -3142,7 +3166,7 @@ fig-input-angle {
3142
3166
 
3143
3167
  /* Layer */
3144
3168
  fig-layer {
3145
- --indent: var(--spacer-4);
3169
+ --indent: var(--spacer-2);
3146
3170
  display: block;
3147
3171
  color: var(--figma-color-text);
3148
3172
  position: relative;
@@ -3154,14 +3178,11 @@ fig-layer {
3154
3178
  & > .fig-layer-row {
3155
3179
  & > .fig-layer-chevron {
3156
3180
  visibility: visible;
3181
+ margin-left: calc(-1 * (var(--spacer-3) + var(--spacer-3) + 6px));
3182
+ margin-right: 0;
3157
3183
  }
3158
3184
  }
3159
3185
  }
3160
- &:not(:has(fig-layer)):not(:has(.fig-layer-icon)) {
3161
- & > .fig-layer-row {
3162
- padding-left: var(--spacer-3);
3163
- }
3164
- }
3165
3186
 
3166
3187
  &:not(:has(.fig-layer-icon)) {
3167
3188
  & > .fig-layer-row {
@@ -3186,7 +3207,7 @@ fig-layer {
3186
3207
  mask-position: center;
3187
3208
  display: flex;
3188
3209
  visibility: hidden;
3189
- margin-left: calc(-1 * (var(--spacer-3) + var(--spacer-1) + 2px));
3210
+ margin-left: calc(-1 * (var(--spacer-3) + var(--spacer-3)));
3190
3211
  margin-right: calc(-1 * (var(--spacer-1) + 2px));
3191
3212
  background: var(--figma-color-text-tertiary);
3192
3213
  width: var(--spacer-3);
package/fig.js CHANGED
@@ -1813,17 +1813,24 @@ class FigPopup extends HTMLDialogElement {
1813
1813
 
1814
1814
  const anchorCenterX = anchorRect.left + anchorRect.width / 2;
1815
1815
  const anchorCenterY = anchorRect.top + anchorRect.height / 2;
1816
+ const measuredRect = this.getBoundingClientRect();
1817
+ const rect =
1818
+ measuredRect.width > 0 && measuredRect.height > 0 ? measuredRect : popupRect;
1819
+ // Always use the rendered popup rect so beak alignment matches real final placement.
1820
+ const resolvedLeft = rect.left;
1821
+ const resolvedTop = rect.top;
1822
+ const edgeInset = 10;
1816
1823
 
1817
1824
  let beakOffset;
1818
1825
  if (beakSide === "top" || beakSide === "bottom") {
1819
- beakOffset = anchorCenterX - left;
1820
- const min = 8;
1821
- const max = Math.max(min, popupRect.width - 8);
1826
+ beakOffset = anchorCenterX - resolvedLeft;
1827
+ const min = edgeInset;
1828
+ const max = Math.max(min, rect.width - edgeInset);
1822
1829
  beakOffset = Math.min(max, Math.max(min, beakOffset));
1823
1830
  } else {
1824
- beakOffset = anchorCenterY - top;
1825
- const min = 8;
1826
- const max = Math.max(min, popupRect.height - 8);
1831
+ beakOffset = anchorCenterY - resolvedTop;
1832
+ const min = edgeInset;
1833
+ const max = Math.max(min, rect.height - edgeInset);
1827
1834
  beakOffset = Math.min(max, Math.max(min, beakOffset));
1828
1835
  }
1829
1836
 
@@ -2740,6 +2747,8 @@ class FigInputText extends HTMLElement {
2740
2747
  this.type = this.getAttribute("type") || "text";
2741
2748
  this.placeholder = this.getAttribute("placeholder") || "";
2742
2749
  this.name = this.getAttribute("name") || null;
2750
+ this.readonly =
2751
+ this.hasAttribute("readonly") && this.getAttribute("readonly") !== "false";
2743
2752
 
2744
2753
  if (this.type === "number") {
2745
2754
  if (this.getAttribute("step")) {
@@ -2796,6 +2805,7 @@ class FigInputText extends HTMLElement {
2796
2805
  }
2797
2806
 
2798
2807
  this.input = this.querySelector("input,textarea");
2808
+ this.input.readOnly = this.readonly;
2799
2809
 
2800
2810
  if (this.type === "number") {
2801
2811
  if (this.getAttribute("min")) {
@@ -2931,6 +2941,7 @@ class FigInputText extends HTMLElement {
2931
2941
  "placeholder",
2932
2942
  "label",
2933
2943
  "disabled",
2944
+ "readonly",
2934
2945
  "type",
2935
2946
  "step",
2936
2947
  "min",
@@ -2947,6 +2958,10 @@ class FigInputText extends HTMLElement {
2947
2958
  this.disabled = this.input.disabled =
2948
2959
  newValue !== null && newValue !== "false";
2949
2960
  break;
2961
+ case "readonly":
2962
+ this.readonly = newValue !== null && newValue !== "false";
2963
+ this.input.readOnly = this.readonly;
2964
+ break;
2950
2965
  case "transform":
2951
2966
  if (this.type === "number") {
2952
2967
  this.transform = Number(newValue) || 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "2.31.2",
3
+ "version": "2.33.0",
4
4
  "description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
5
5
  "author": "Rogie King",
6
6
  "license": "MIT",
@@ -12,8 +12,7 @@
12
12
  "./fig.js": "./fig.js",
13
13
  "./fig.css": "./fig.css",
14
14
  "./base.css": "./base.css",
15
- "./components.css": "./components.css",
16
- "./propkit.html": "./propkit.html"
15
+ "./components.css": "./components.css"
17
16
  },
18
17
  "files": [
19
18
  "fig.js",
@@ -21,7 +20,6 @@
21
20
  "base.css",
22
21
  "components.css",
23
22
  "index.html",
24
- "propkit.html",
25
23
  "dist/",
26
24
  ".cursor/skills/",
27
25
  "README.md",
@@ -33,8 +31,8 @@
33
31
  "scripts": {
34
32
  "dev": "bun --hot server.ts",
35
33
  "build": "bun build fig.js --minify --outdir dist",
36
- "dev:propkit": "cd propkit && npm run dev",
37
- "build:propkit": "cd propkit && npm run build"
34
+ "dev:playground": "cd playground && npm run dev",
35
+ "build:playground": "cd playground && npm run build"
38
36
  },
39
37
  "repository": {
40
38
  "type": "git",