@rogieking/figui3 3.15.0 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/components.css +93 -60
  2. package/fig.js +172 -75
  3. package/package.json +1 -1
package/components.css CHANGED
@@ -1224,7 +1224,7 @@ fig-chit {
1224
1224
  --padding: 0px;
1225
1225
  }
1226
1226
 
1227
- &[disabled] {
1227
+ &[disabled]:not([disabled="false"]) {
1228
1228
  pointer-events: none;
1229
1229
  }
1230
1230
 
@@ -1373,9 +1373,9 @@ fig-easing-curve {
1373
1373
  --easing-bezier-handle-radius: 5;
1374
1374
  --easing-spring-handle-radius: 5;
1375
1375
  --easing-handle-fill: var(--figma-color-border-strong);
1376
- --easing-duration-bar-width: 5;
1376
+ --easing-duration-bar-width: 7;
1377
1377
  --easing-duration-bar-height: 16;
1378
- --easing-duration-bar-radius: 3;
1378
+ --easing-duration-bar-radius: 4;
1379
1379
  --aspect-ratio: 1 / 1;
1380
1380
 
1381
1381
  width: 100%;
@@ -1438,17 +1438,6 @@ fig-easing-curve {
1438
1438
  overflow: visible;
1439
1439
  pointer-events: all;
1440
1440
  cursor: default;
1441
-
1442
- fig-handle {
1443
- --width: calc(var(--easing-bezier-handle-radius) * 2px);
1444
- --height: calc(var(--easing-bezier-handle-radius) * 2px);
1445
- pointer-events: all;
1446
- cursor: default;
1447
- }
1448
-
1449
- &:active fig-handle {
1450
- cursor: grabbing;
1451
- }
1452
1441
  }
1453
1442
  .fig-easing-curve-endpoint {
1454
1443
  fill: var(--easing-handle-fill);
@@ -3095,6 +3084,10 @@ fig-input-fill {
3095
3084
  outline: 1px solid var(--figma-color-border-selected) !important;
3096
3085
  outline-offset: -1px !important;
3097
3086
  }
3087
+ &:hover {
3088
+ outline: 1px solid var(--figma-color-border);
3089
+ outline-offset: -1px !important;
3090
+ }
3098
3091
 
3099
3092
  & > .input-combo > fig-chit:not(:only-child),
3100
3093
  & > .input-combo > fig-chit:not(:only-child) input,
@@ -3179,11 +3172,10 @@ fig-input-gradient {
3179
3172
  }
3180
3173
 
3181
3174
  fig-input-palette {
3182
- display: inline-grid !important;
3183
- grid-template-columns: 1fr 24px;
3184
- grid-template-areas: "inputs button";
3185
- gap: var(--spacer-1);
3186
- width: 100%;
3175
+ display: inline-flex !important;
3176
+ flex-direction: column;
3177
+ gap: var(--spacer-2);
3178
+ min-width: 0;
3187
3179
 
3188
3180
  .palette-colors {
3189
3181
  display: flex;
@@ -3199,15 +3191,19 @@ fig-input-palette {
3199
3191
  .palette-add-btn {
3200
3192
  grid-area: button;
3201
3193
  }
3194
+ .palette-colors-inline {
3195
+ display: inline-grid !important;
3196
+ grid-template-columns: 1fr 24px;
3197
+ grid-template-areas: "inputs button";
3198
+ border-radius: var(--radius-medium);
3199
+ background-color: var(--figma-color-bg-secondary);
3200
+ width: 100%;
3202
3201
 
3203
- &:not([expanded]),
3204
- &[expanded="false"] {
3205
3202
  .palette-colors {
3206
3203
  display: flex;
3207
3204
  background-color: var(--figma-color-bg-secondary);
3208
-
3209
- > fig-input-color {
3210
- display: contents;
3205
+ fig-input-color {
3206
+ width: 100%;
3211
3207
  }
3212
3208
 
3213
3209
  fig-chit {
@@ -3228,27 +3224,39 @@ fig-input-palette {
3228
3224
  }
3229
3225
  }
3230
3226
  }
3227
+ &[expanded]:not([expanded="false"]):not([edit="false"]) {
3228
+ .palette-colors-expanded {
3229
+ display: flex;
3230
+ }
3231
+ }
3232
+ &[add="false"] {
3233
+ .palette-colors-expanded,
3234
+ .palette-colors-inline {
3235
+ grid-template-areas: "inputs inputs";
3236
+ }
3237
+ }
3238
+ .palette-colors-expanded {
3239
+ display: none;
3240
+ flex-direction: column;
3241
+ overflow: visible;
3242
+ border-radius: 0;
3243
+ gap: var(--spacer-2);
3244
+ width: 100%;
3231
3245
 
3232
- &[expanded]:not([expanded="false"]) {
3233
- .palette-colors {
3234
- flex-direction: column;
3235
- overflow: visible;
3236
- border-radius: 0;
3237
- gap: var(--spacer-2);
3238
- background-color: transparent;
3239
-
3240
- > fig-input-color {
3241
- min-width: 0;
3242
- }
3246
+ > fig-input-color {
3247
+ min-width: 0;
3248
+ }
3243
3249
 
3244
- fig-chit {
3245
- --border-radius: var(--radius-medium);
3246
- width: auto !important;
3247
- height: var(--size);
3248
- border-radius: var(--radius-medium) !important;
3249
- }
3250
+ fig-chit {
3251
+ --border-radius: var(--radius-medium);
3252
+ width: auto !important;
3253
+ height: var(--size);
3254
+ border-radius: var(--radius-medium) !important;
3250
3255
  }
3251
3256
  }
3257
+
3258
+ &[expanded]:not([expanded="false"]) {
3259
+ }
3252
3260
  }
3253
3261
 
3254
3262
  fig-field[direction="horizontal"]:has(> fig-input-palette) {
@@ -4584,8 +4592,20 @@ fig-chooser {
4584
4592
  display: flex;
4585
4593
  flex-direction: column;
4586
4594
  gap: 1px;
4587
- overflow: hidden auto;
4595
+ overflow: visible auto;
4588
4596
  scrollbar-width: none;
4597
+ scroll-snap-type: y mandatory;
4598
+
4599
+ > * {
4600
+ scroll-snap-align: start;
4601
+ }
4602
+
4603
+ &[padding="false"] {
4604
+ gap: var(--spacer-2);
4605
+ fig-choice {
4606
+ --fig-choice-padding: 0px;
4607
+ }
4608
+ }
4589
4609
 
4590
4610
  &[overflow="scrollbar"] {
4591
4611
  scrollbar-width: thin;
@@ -4753,12 +4773,15 @@ fig-chooser {
4753
4773
  }
4754
4774
 
4755
4775
  fig-choice {
4756
- --fig-choice-selection-ring-width: 1px;
4776
+ --fig-choice-selection-ring-width: 1.25px;
4757
4777
  --fig-choice-padding: var(--spacer-2);
4778
+ --fig-choice-border-radius: calc(
4779
+ var(--radius-medium) + var(--fig-choice-padding)
4780
+ );
4758
4781
  display: flex;
4759
4782
  align-items: center;
4760
4783
  flex-direction: column;
4761
- border-radius: var(--radius-large);
4784
+ border-radius: var(--fig-choice-border-radius);
4762
4785
  gap: var(--spacer-2);
4763
4786
  outline: none;
4764
4787
  border: 1px solid transparent;
@@ -4770,14 +4793,25 @@ fig-choice {
4770
4793
  background-color: var(--figma-color-bg-secondary);
4771
4794
  }
4772
4795
 
4796
+ &[padding="false"] {
4797
+ --fig-choice-padding: 0px;
4798
+ }
4799
+
4773
4800
  &[selected] {
4774
- background-color: var(--figma-color-bg-secondary);
4775
- & > fig-image {
4776
- border-radius: var(--radius-medium);
4801
+ position: relative;
4802
+ &::after {
4803
+ content: "";
4804
+ position: absolute;
4805
+ inset: 0;
4806
+ border-radius: var(--fig-choice-border-radius);
4807
+ pointer-events: none;
4808
+ z-index: 1;
4809
+ box-shadow: inset 0 0 0 4px var(--figma-color-bg);
4777
4810
  outline: var(--fig-choice-selection-ring-width) solid
4778
4811
  var(--figma-color-border-selected);
4779
- outline-offset: 0;
4812
+ outline-offset: calc(var(--fig-choice-selection-ring-width) * -1);
4780
4813
  }
4814
+ background-color: var(--figma-color-bg-secondary);
4781
4815
  }
4782
4816
 
4783
4817
  &[disabled]:not([disabled="false"]),
@@ -4793,14 +4827,14 @@ fig-handle {
4793
4827
  --height: 0.875rem;
4794
4828
  --fill: var(--figma-color-bg-brand);
4795
4829
  --border-radius: 50%;
4796
- --ring-width: 1.25px;
4797
- --box-shadow:
4798
- inset 0 0 0 var(--ring-width) var(--handle-color),
4799
- 0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--elevation-100-canvas);
4830
+ --ring-width: 0.125rem;
4831
+ --box-shadow: 0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--elevation-100-canvas);
4800
4832
  --outline: none;
4801
4833
  --border: none;
4802
4834
 
4803
- display: inline-grid;
4835
+ display: grid;
4836
+ margin: 0;
4837
+ padding: 0;
4804
4838
  place-items: center;
4805
4839
  width: var(--width);
4806
4840
  height: var(--height);
@@ -4813,8 +4847,8 @@ fig-handle {
4813
4847
  &::before {
4814
4848
  content: "";
4815
4849
  color-scheme: light only;
4816
- width: calc(var(--width) - 4px);
4817
- height: calc(var(--height) - 4px);
4850
+ width: calc(var(--width) - var(--ring-width) * 2);
4851
+ height: calc(var(--height) - var(--ring-width) * 2);
4818
4852
  background: var(--fill);
4819
4853
  border-radius: var(--border-radius);
4820
4854
  box-shadow: inset 0 0 0 1px var(--figma-color-bordertranslucent);
@@ -4822,8 +4856,8 @@ fig-handle {
4822
4856
  }
4823
4857
 
4824
4858
  &[size="small"] {
4825
- --width: 0.5625rem;
4826
- --height: 0.5625rem;
4859
+ --width: 11px;
4860
+ --height: 11px;
4827
4861
  }
4828
4862
 
4829
4863
  &[drag]:not([drag="false"]) {
@@ -4832,14 +4866,13 @@ fig-handle {
4832
4866
  }
4833
4867
  &:hover,
4834
4868
  &[selected]:not([selected="false"]) {
4835
- outline: var(--ring-width) solid var(--figma-color-border-selected);
4869
+ outline: 1px solid var(--figma-color-border-selected);
4836
4870
  outline-offset: 0;
4837
4871
  }
4838
4872
 
4839
4873
  &[disabled]:not([disabled="false"]),
4840
4874
  &[aria-disabled="true"] {
4841
4875
  pointer-events: none;
4842
- opacity: 0.4;
4843
4876
  cursor: default;
4844
4877
  }
4845
4878
 
@@ -4849,7 +4882,7 @@ fig-handle {
4849
4882
 
4850
4883
  fig-color-tip {
4851
4884
  position: absolute;
4852
- bottom: calc(100% + 6px);
4885
+ bottom: calc(100% + 2px);
4853
4886
  left: 50%;
4854
4887
  transform: translateX(-50%);
4855
4888
  z-index: 10;
package/fig.js CHANGED
@@ -4519,6 +4519,8 @@ class FigInputColor extends HTMLElement {
4519
4519
  const hidePicker = this.picker === "false";
4520
4520
  const showAlpha = this.getAttribute("alpha") === "true";
4521
4521
  const fpAttrs = this.#buildFillPickerAttrs();
4522
+ const disabled = this.#disabled;
4523
+ const disabledAttr = disabled ? " disabled" : "";
4522
4524
 
4523
4525
  let html = ``;
4524
4526
  const showText = this.getAttribute("text") === "true";
@@ -4526,7 +4528,7 @@ class FigInputColor extends HTMLElement {
4526
4528
  let label = `<fig-input-text
4527
4529
  type="text"
4528
4530
  placeholder="000000"
4529
- value="${this.hexOpaque.slice(1).toUpperCase()}">
4531
+ value="${this.hexOpaque.slice(1).toUpperCase()}"${disabledAttr}>
4530
4532
  </fig-input-text>`;
4531
4533
  if (showAlpha) {
4532
4534
  label += `<fig-tooltip text="Opacity">
@@ -4535,7 +4537,7 @@ class FigInputColor extends HTMLElement {
4535
4537
  min="0"
4536
4538
  max="100"
4537
4539
  value="${this.#alphaPercent}"
4538
- units="%">
4540
+ units="%"${disabledAttr}>
4539
4541
  </fig-input-number>
4540
4542
  </fig-tooltip>`;
4541
4543
  }
@@ -4547,8 +4549,8 @@ class FigInputColor extends HTMLElement {
4547
4549
  showAlpha ? "" : 'alpha="false"'
4548
4550
  } value='{"type":"solid","color":"${this.hexOpaque}","opacity":${
4549
4551
  this.#alphaPercent
4550
- }}'></fig-fill-picker>`
4551
- : `<fig-chit background="${this.hexOpaque}" alpha="${this.rgba.a}"></fig-chit>`;
4552
+ }}'${disabledAttr}></fig-fill-picker>`
4553
+ : `<fig-chit background="${this.hexOpaque}" alpha="${this.rgba.a}"${disabledAttr}></fig-chit>`;
4552
4554
  }
4553
4555
 
4554
4556
  html = `<div class="input-combo">
@@ -4564,8 +4566,8 @@ class FigInputColor extends HTMLElement {
4564
4566
  showAlpha ? "" : 'alpha="false"'
4565
4567
  } value='{"type":"solid","color":"${this.hexOpaque}","opacity":${
4566
4568
  this.#alphaPercent
4567
- }}'></fig-fill-picker>`
4568
- : `<fig-chit background="${this.hexOpaque}" alpha="${this.rgba.a}"></fig-chit>`;
4569
+ }}'${disabledAttr}></fig-fill-picker>`
4570
+ : `<fig-chit background="${this.hexOpaque}" alpha="${this.rgba.a}"${disabledAttr}></fig-chit>`;
4569
4571
  }
4570
4572
  }
4571
4573
  this.innerHTML = html;
@@ -4759,7 +4761,7 @@ class FigInputColor extends HTMLElement {
4759
4761
  }
4760
4762
 
4761
4763
  static get observedAttributes() {
4762
- return ["value", "style", "mode", "picker", "experimental", "alpha", "text"];
4764
+ return ["value", "style", "mode", "picker", "experimental", "alpha", "text", "disabled"];
4763
4765
  }
4764
4766
 
4765
4767
  get mode() {
@@ -4813,6 +4815,26 @@ class FigInputColor extends HTMLElement {
4813
4815
  case "text":
4814
4816
  if (this.isConnected) this.#buildUI();
4815
4817
  break;
4818
+ case "disabled":
4819
+ this.#syncDisabled();
4820
+ break;
4821
+ }
4822
+ }
4823
+
4824
+ get #disabled() {
4825
+ return this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
4826
+ }
4827
+
4828
+ #syncDisabled() {
4829
+ const disabled = this.#disabled;
4830
+ for (const child of [this.#swatch, this.#textInput, this.#alphaInput]) {
4831
+ if (!child) continue;
4832
+ if (disabled) child.setAttribute("disabled", "");
4833
+ else child.removeAttribute("disabled");
4834
+ }
4835
+ if (this.#fillPicker) {
4836
+ if (disabled) this.#fillPicker.setAttribute("disabled", "");
4837
+ else this.#fillPicker.removeAttribute("disabled");
4816
4838
  }
4817
4839
  }
4818
4840
 
@@ -5132,6 +5154,15 @@ class FigInputFill extends HTMLElement {
5132
5154
  .join(" ");
5133
5155
  }
5134
5156
 
5157
+ #syncDisabled() {
5158
+ const disabled = this.hasAttribute("disabled");
5159
+ for (const child of [this.#fillPicker, this.#opacityInput, this.#hexInput]) {
5160
+ if (!child) continue;
5161
+ if (disabled) child.setAttribute("disabled", "");
5162
+ else child.removeAttribute("disabled");
5163
+ }
5164
+ }
5165
+
5135
5166
  #render() {
5136
5167
  const disabled = this.hasAttribute("disabled");
5137
5168
  const fillPickerValue = JSON.stringify(this.value);
@@ -5664,10 +5695,7 @@ class FigInputFill extends HTMLElement {
5664
5695
  }
5665
5696
  break;
5666
5697
  case "disabled":
5667
- // Re-render to update disabled state
5668
- if (this.#fillPicker) {
5669
- this.#render();
5670
- }
5698
+ this.#syncDisabled();
5671
5699
  break;
5672
5700
  case "mode":
5673
5701
  case "experimental":
@@ -5698,17 +5726,22 @@ customElements.define("fig-input-fill", FigInputFill);
5698
5726
  */
5699
5727
  class FigInputPalette extends HTMLElement {
5700
5728
  #colors = [];
5701
- #pickers = [];
5729
+ #inlinePickers = [];
5730
+ #expandedPickers = [];
5702
5731
  #renderRAF = null;
5703
5732
 
5704
5733
  static get observedAttributes() {
5705
- return ["value", "disabled", "min", "max", "expanded"];
5734
+ return ["value", "disabled", "min", "max", "expanded", "add"];
5706
5735
  }
5707
5736
 
5708
5737
  get #expanded() {
5709
5738
  return this.hasAttribute("expanded") && this.getAttribute("expanded") !== "false";
5710
5739
  }
5711
5740
 
5741
+ get #showAdd() {
5742
+ return !this.hasAttribute("add") || this.getAttribute("add") !== "false";
5743
+ }
5744
+
5712
5745
  get #min() {
5713
5746
  const v = parseInt(this.getAttribute("min"));
5714
5747
  return isNaN(v) ? 2 : v;
@@ -5733,7 +5766,8 @@ class FigInputPalette extends HTMLElement {
5733
5766
  cancelAnimationFrame(this.#renderRAF);
5734
5767
  this.#renderRAF = null;
5735
5768
  }
5736
- this.#pickers = [];
5769
+ this.#inlinePickers = [];
5770
+ this.#expandedPickers = [];
5737
5771
  }
5738
5772
 
5739
5773
  attributeChangedCallback(name, oldValue, newValue) {
@@ -5750,6 +5784,7 @@ class FigInputPalette extends HTMLElement {
5750
5784
  case "min":
5751
5785
  case "max":
5752
5786
  case "expanded":
5787
+ case "add":
5753
5788
  this.#render();
5754
5789
  break;
5755
5790
  }
@@ -5826,32 +5861,50 @@ class FigInputPalette extends HTMLElement {
5826
5861
  const disabled = this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
5827
5862
 
5828
5863
  this.innerHTML = "";
5829
- this.#pickers = [];
5864
+ this.#inlinePickers = [];
5865
+ this.#expandedPickers = [];
5866
+
5867
+ const inlineWrap = document.createElement("div");
5868
+ inlineWrap.className = "palette-colors-inline";
5830
5869
 
5831
5870
  const wrap = document.createElement("div");
5832
5871
  wrap.className = "palette-colors";
5833
5872
  this.#colors.forEach((entry, i) => {
5834
- wrap.appendChild(this.#createPicker(entry, i, disabled));
5873
+ wrap.appendChild(this.#createPicker(entry, i, disabled, { inline: true }));
5835
5874
  });
5836
- this.appendChild(wrap);
5875
+ inlineWrap.appendChild(wrap);
5876
+
5877
+ if (this.#showAdd) this.#createAddButton(disabled, inlineWrap);
5878
+ this.appendChild(inlineWrap);
5837
5879
 
5838
- this.#createAddButton(disabled);
5880
+ const expandedWrap = document.createElement("div");
5881
+ expandedWrap.className = "palette-colors-expanded";
5882
+ this.#colors.forEach((entry, i) => {
5883
+ expandedWrap.appendChild(this.#createPicker(entry, i, disabled));
5884
+ });
5885
+ this.appendChild(expandedWrap);
5839
5886
  }
5840
5887
 
5841
- #createPicker(entry, index, disabled) {
5888
+ #createPicker(entry, index, disabled, { inline = false } = {}) {
5842
5889
  const hexAlpha = entry.alpha < 1
5843
5890
  ? entry.color + Math.round(entry.alpha * 255).toString(16).padStart(2, "0")
5844
5891
  : entry.color;
5845
- const expanded = this.#expanded;
5846
5892
  const ic = document.createElement("fig-input-color");
5847
5893
  ic.setAttribute("value", hexAlpha);
5848
- ic.setAttribute("text", expanded ? "true" : "false");
5849
5894
  ic.setAttribute("picker", "figma");
5850
- ic.setAttribute("alpha", "true");
5851
5895
  ic.setAttribute("picker-anchor", "self");
5852
- if (expanded) ic.setAttribute("full", "");
5896
+ if (inline) {
5897
+ ic.setAttribute("text", "false");
5898
+ ic.setAttribute("alpha", "true");
5899
+ } else {
5900
+ ic.setAttribute("text", "true");
5901
+ ic.setAttribute("alpha", "true");
5902
+ ic.setAttribute("full", "");
5903
+ }
5853
5904
  if (disabled) ic.setAttribute("disabled", "");
5854
5905
 
5906
+ const siblingList = inline ? this.#expandedPickers : this.#inlinePickers;
5907
+
5855
5908
  const updateFromPicker = (e) => {
5856
5909
  e.stopPropagation();
5857
5910
  const el = e.currentTarget;
@@ -5859,6 +5912,14 @@ class FigInputPalette extends HTMLElement {
5859
5912
  color: el.hexOpaque || this.#colors[index].color,
5860
5913
  alpha: el.rgba ? el.rgba.a : this.#colors[index].alpha,
5861
5914
  };
5915
+ const sibling = siblingList[index];
5916
+ if (sibling) {
5917
+ const entry = this.#colors[index];
5918
+ const hex = entry.alpha < 1
5919
+ ? entry.color + Math.round(entry.alpha * 255).toString(16).padStart(2, "0")
5920
+ : entry.color;
5921
+ sibling.setAttribute("value", hex);
5922
+ }
5862
5923
  };
5863
5924
 
5864
5925
  ic.addEventListener("input", (e) => {
@@ -5871,11 +5932,12 @@ class FigInputPalette extends HTMLElement {
5871
5932
  this.#emitChange();
5872
5933
  });
5873
5934
 
5874
- this.#pickers.push(ic);
5935
+ if (inline) this.#inlinePickers.push(ic);
5936
+ else this.#expandedPickers.push(ic);
5875
5937
  return ic;
5876
5938
  }
5877
5939
 
5878
- #createAddButton(disabled) {
5940
+ #createAddButton(disabled, parent = this) {
5879
5941
  const atMax = this.#colors.length >= this.#max;
5880
5942
  const addBtn = document.createElement("fig-button");
5881
5943
  addBtn.setAttribute("variant", "ghost");
@@ -5892,19 +5954,21 @@ class FigInputPalette extends HTMLElement {
5892
5954
  const tooltip = document.createElement("fig-tooltip");
5893
5955
  tooltip.setAttribute("text", "Add color");
5894
5956
  tooltip.appendChild(addBtn);
5895
- this.appendChild(tooltip);
5957
+ parent.appendChild(tooltip);
5896
5958
  }
5897
5959
 
5898
5960
  #addColor(entry) {
5899
5961
  this.#colors.push(entry);
5900
5962
  const disabled = this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
5901
- const ic = this.#createPicker(entry, this.#colors.length - 1, disabled);
5963
+ const index = this.#colors.length - 1;
5964
+
5965
+ const inlineIc = this.#createPicker(entry, index, disabled, { inline: true });
5902
5966
  const wrap = this.querySelector(".palette-colors");
5903
- if (wrap) {
5904
- wrap.appendChild(ic);
5905
- } else {
5906
- this.appendChild(ic);
5907
- }
5967
+ if (wrap) wrap.appendChild(inlineIc);
5968
+
5969
+ const expandedIc = this.#createPicker(entry, index, disabled);
5970
+ const expandedWrap = this.querySelector(".palette-colors-expanded");
5971
+ if (expandedWrap) expandedWrap.appendChild(expandedIc);
5908
5972
 
5909
5973
  if (this.#colors.length >= this.#max) {
5910
5974
  const addBtn = this.querySelector(".palette-add-btn");
@@ -5914,17 +5978,19 @@ class FigInputPalette extends HTMLElement {
5914
5978
  }
5915
5979
 
5916
5980
  #updateChit(index) {
5917
- const ic = this.#pickers[index];
5918
- if (!ic) return;
5919
5981
  const entry = this.#colors[index];
5982
+ if (!entry) return;
5920
5983
  const hexAlpha = entry.alpha < 1
5921
5984
  ? entry.color + Math.round(entry.alpha * 255).toString(16).padStart(2, "0")
5922
5985
  : entry.color;
5923
- ic.setAttribute("value", hexAlpha);
5986
+ const inl = this.#inlinePickers[index];
5987
+ if (inl) inl.setAttribute("value", hexAlpha);
5988
+ const exp = this.#expandedPickers[index];
5989
+ if (exp) exp.setAttribute("value", hexAlpha);
5924
5990
  }
5925
5991
 
5926
5992
  #syncPickers() {
5927
- if (this.#pickers.length !== this.#colors.length) {
5993
+ if (this.#inlinePickers.length !== this.#colors.length) {
5928
5994
  this.#render();
5929
5995
  return;
5930
5996
  }
@@ -5935,7 +6001,7 @@ class FigInputPalette extends HTMLElement {
5935
6001
 
5936
6002
  #syncDisabled() {
5937
6003
  const disabled = this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
5938
- this.#pickers.forEach((fp) => {
6004
+ [...this.#inlinePickers, ...this.#expandedPickers].forEach((fp) => {
5939
6005
  if (disabled) fp.setAttribute("disabled", "");
5940
6006
  else fp.removeAttribute("disabled");
5941
6007
  });
@@ -5975,6 +6041,7 @@ customElements.define("fig-input-palette", FigInputPalette);
5975
6041
  * @fires change - When the gradient value is committed
5976
6042
  */
5977
6043
  class FigInputGradient extends HTMLElement {
6044
+ static SHIFT_SNAP = 5;
5978
6045
  #chit;
5979
6046
  #track;
5980
6047
  #handleDragging = false;
@@ -6046,7 +6113,7 @@ class FigInputGradient extends HTMLElement {
6046
6113
  const idx = parseInt(selected.dataset.stopIndex, 10);
6047
6114
  if (isNaN(idx) || !this.#gradient.stops[idx]) return;
6048
6115
  e.preventDefault();
6049
- const delta = (e.key === "ArrowRight" ? 1 : -1) * (e.shiftKey ? 10 : 1);
6116
+ const delta = (e.key === "ArrowRight" ? 1 : -1) * (e.shiftKey ? FigInputGradient.SHIFT_SNAP : 1);
6050
6117
  const stop = this.#gradient.stops[idx];
6051
6118
  stop.position = Math.max(0, Math.min(100, stop.position + delta));
6052
6119
  selected.setAttribute("value", `${stop.position}% 50%`);
@@ -6242,15 +6309,15 @@ class FigInputGradient extends HTMLElement {
6242
6309
  this.#syncChit();
6243
6310
  this.#emitInput();
6244
6311
  this.#emitChange();
6245
- this.#track?.querySelectorAll("fig-handle[selected]").forEach((h) => {
6246
- h.removeAttribute("selected");
6247
- });
6248
6312
  }
6249
6313
 
6250
6314
  #onTrackDblClick = (e) => {
6251
6315
  if (!this.#track) return;
6252
6316
  if (!e.target.closest("fig-handle:not(.fig-input-gradient-ghost)")) return;
6253
6317
  this.#distributeStops();
6318
+ this.#track.querySelectorAll("fig-handle[selected]").forEach((h) => {
6319
+ h.removeAttribute("selected");
6320
+ });
6254
6321
  };
6255
6322
 
6256
6323
  #onTrackClick = (e) => {
@@ -6258,7 +6325,15 @@ class FigInputGradient extends HTMLElement {
6258
6325
  if (this.#handleDragging) return;
6259
6326
  if (e.target.closest("fig-handle:not(.fig-input-gradient-ghost)")) {
6260
6327
  if (e.shiftKey) {
6328
+ const clickedHandle = e.target.closest("fig-handle");
6329
+ const stopIdx = parseInt(clickedHandle?.dataset.stopIndex, 10);
6261
6330
  this.#distributeStops();
6331
+ if (!isNaN(stopIdx)) {
6332
+ this.#track.querySelectorAll("fig-handle:not(.fig-input-gradient-ghost)").forEach((h) => {
6333
+ if (parseInt(h.dataset.stopIndex, 10) === stopIdx) h.select();
6334
+ else h.deselect();
6335
+ });
6336
+ }
6262
6337
  e.stopPropagation();
6263
6338
  }
6264
6339
  return;
@@ -6307,6 +6382,7 @@ class FigInputGradient extends HTMLElement {
6307
6382
  for (let i = 0; i < stops.length; i++) {
6308
6383
  const h = handles[i];
6309
6384
  const stop = stops[i];
6385
+ h.dataset.stopIndex = i;
6310
6386
  h.setAttribute("value", `${stop.position}% 50%`);
6311
6387
  h.setAttribute("color", stop.color);
6312
6388
  const tip = h.closest("fig-tooltip");
@@ -6387,7 +6463,7 @@ class FigInputGradient extends HTMLElement {
6387
6463
  let position = rawPosition;
6388
6464
  const trackW = this.#track.getBoundingClientRect().width;
6389
6465
  if (e.detail?.shiftKey) {
6390
- position = Math.round(position / 10) * 10;
6466
+ position = Math.round(position / FigInputGradient.SHIFT_SNAP) * FigInputGradient.SHIFT_SNAP;
6391
6467
  } else {
6392
6468
  const snapPct = trackW > 0 ? (5 / trackW) * 100 : 0;
6393
6469
  for (let i = 0; i < this.#gradient.stops.length; i++) {
@@ -6515,10 +6591,24 @@ class FigInputGradient extends HTMLElement {
6515
6591
  this.#syncHandles();
6516
6592
  break;
6517
6593
  case "disabled":
6518
- this.#render();
6594
+ this.#syncDisabled();
6519
6595
  break;
6520
6596
  }
6521
6597
  }
6598
+
6599
+ #syncDisabled() {
6600
+ const disabled = this.hasAttribute("disabled");
6601
+ if (this.#chit) {
6602
+ if (disabled) this.#chit.setAttribute("disabled", "");
6603
+ else this.#chit.removeAttribute("disabled");
6604
+ }
6605
+ if (this.#track) {
6606
+ for (const handle of this.#track.querySelectorAll("fig-handle")) {
6607
+ if (disabled) handle.setAttribute("disabled", "");
6608
+ else handle.removeAttribute("disabled");
6609
+ }
6610
+ }
6611
+ }
6522
6612
  }
6523
6613
  customElements.define("fig-input-gradient", FigInputGradient);
6524
6614
 
@@ -7497,11 +7587,10 @@ class FigEasingCurve extends HTMLElement {
7497
7587
  #bounds = null;
7498
7588
  #diagonal = null;
7499
7589
  #resizeObserver = null;
7500
- #bezierHandleRadius = 3.625;
7590
+ #bezierHandleRadius = 5;
7501
7591
  #bezierEndpointRadius = 2;
7502
- #springHandleRadius = 3.625;
7503
- #durationBarWidth = 6;
7504
- #durationBarHeight = 16;
7592
+ #durationBarWidth = 10;
7593
+ #durationBarHeight = 10;
7505
7594
  #durationBarRadius = 3;
7506
7595
 
7507
7596
  static PRESETS = [
@@ -7818,6 +7907,7 @@ class FigEasingCurve extends HTMLElement {
7818
7907
  this.#syncMetricsFromCSS();
7819
7908
  this.innerHTML = this.#getInnerHTML();
7820
7909
  this.#cacheRefs();
7910
+ this.#syncHandleSizes();
7821
7911
  this.#syncViewportSize();
7822
7912
  this.#updatePaths();
7823
7913
  this.#setupEvents();
@@ -7857,8 +7947,6 @@ class FigEasingCurve extends HTMLElement {
7857
7947
  const size = 200;
7858
7948
  const dropdown = this.#getDropdownHTML();
7859
7949
 
7860
- const hs = this.#bezierHandleRadius * 2;
7861
-
7862
7950
  if (this.#mode === "spring") {
7863
7951
  const targetY = 40;
7864
7952
  const startY = 180;
@@ -7867,8 +7955,8 @@ class FigEasingCurve extends HTMLElement {
7867
7955
  <line class="fig-easing-curve-target" x1="0" y1="${targetY}" x2="${size}" y2="${targetY}"/>
7868
7956
  <line class="fig-easing-curve-diagonal" x1="0" y1="${startY}" x2="0" y2="${startY}"/>
7869
7957
  <path class="fig-easing-curve-path"/>
7870
- <foreignObject class="fig-easing-curve-handle" data-handle="bounce" width="${hs}" height="${hs}"><fig-handle></fig-handle></foreignObject>
7871
- <foreignObject class="fig-easing-curve-handle fig-easing-curve-duration-bar" data-handle="duration" width="${this.#durationBarWidth}" height="${this.#durationBarHeight}"><fig-handle></fig-handle></foreignObject>
7958
+ <foreignObject class="fig-easing-curve-handle" data-handle="bounce" width="20" height="20"><fig-handle size="small"></fig-handle></foreignObject>
7959
+ <foreignObject class="fig-easing-curve-handle fig-easing-curve-duration-bar" data-handle="duration" width="20" height="20"><fig-handle size="small"></fig-handle></foreignObject>
7872
7960
  </svg></div>`;
7873
7961
  }
7874
7962
 
@@ -7880,8 +7968,8 @@ class FigEasingCurve extends HTMLElement {
7880
7968
  <path class="fig-easing-curve-path"/>
7881
7969
  <circle class="fig-easing-curve-endpoint" data-endpoint="start" r="${this.#bezierEndpointRadius}"/>
7882
7970
  <circle class="fig-easing-curve-endpoint" data-endpoint="end" r="${this.#bezierEndpointRadius}"/>
7883
- <foreignObject class="fig-easing-curve-handle" data-handle="1" width="${hs}" height="${hs}"><fig-handle></fig-handle></foreignObject>
7884
- <foreignObject class="fig-easing-curve-handle" data-handle="2" width="${hs}" height="${hs}"><fig-handle></fig-handle></foreignObject>
7971
+ <foreignObject class="fig-easing-curve-handle" data-handle="1" width="20" height="20"><fig-handle size="small"></fig-handle></foreignObject>
7972
+ <foreignObject class="fig-easing-curve-handle" data-handle="2" width="20" height="20"><fig-handle size="small"></fig-handle></foreignObject>
7885
7973
  </svg></div>`;
7886
7974
  }
7887
7975
 
@@ -7893,26 +7981,10 @@ class FigEasingCurve extends HTMLElement {
7893
7981
  }
7894
7982
 
7895
7983
  #syncMetricsFromCSS() {
7896
- this.#bezierHandleRadius = this.#readCssNumber(
7897
- "--easing-bezier-handle-radius",
7898
- this.#bezierHandleRadius,
7899
- );
7900
7984
  this.#bezierEndpointRadius = this.#readCssNumber(
7901
7985
  "--easing-bezier-endpoint-radius",
7902
7986
  this.#bezierEndpointRadius,
7903
7987
  );
7904
- this.#springHandleRadius = this.#readCssNumber(
7905
- "--easing-spring-handle-radius",
7906
- this.#springHandleRadius,
7907
- );
7908
- this.#durationBarWidth = this.#readCssNumber(
7909
- "--easing-duration-bar-width",
7910
- this.#durationBarWidth,
7911
- );
7912
- this.#durationBarHeight = this.#readCssNumber(
7913
- "--easing-duration-bar-height",
7914
- this.#durationBarHeight,
7915
- );
7916
7988
  this.#durationBarRadius = this.#readCssNumber(
7917
7989
  "--easing-duration-bar-radius",
7918
7990
  this.#durationBarRadius,
@@ -7938,6 +8010,28 @@ class FigEasingCurve extends HTMLElement {
7938
8010
  this.#diagonal = this.querySelector(".fig-easing-curve-diagonal");
7939
8011
  }
7940
8012
 
8013
+ #syncHandleSizes() {
8014
+ const h1El = this.#handle1?.querySelector("fig-handle");
8015
+ const h2El = this.#handle2?.querySelector("fig-handle");
8016
+ if (h1El) {
8017
+ const w = h1El.offsetWidth || this.#bezierHandleRadius * 2;
8018
+ const h = h1El.offsetHeight || this.#bezierHandleRadius * 2;
8019
+ this.#bezierHandleRadius = Math.max(w, h) / 2;
8020
+ this.#handle1.setAttribute("width", w);
8021
+ this.#handle1.setAttribute("height", h);
8022
+ }
8023
+ if (h2El) {
8024
+ const w = h2El.offsetWidth || this.#durationBarWidth;
8025
+ const h = h2El.offsetHeight || this.#durationBarHeight;
8026
+ if (this.#mode === "spring") {
8027
+ this.#durationBarWidth = w;
8028
+ this.#durationBarHeight = h;
8029
+ }
8030
+ this.#handle2.setAttribute("width", w);
8031
+ this.#handle2.setAttribute("height", h);
8032
+ }
8033
+ }
8034
+
7941
8035
  #setupResizeObserver() {
7942
8036
  if (this.#resizeObserver || !window.ResizeObserver) return;
7943
8037
  this.#resizeObserver = new ResizeObserver(() => {
@@ -12703,7 +12797,7 @@ class FigChooser extends HTMLElement {
12703
12797
  }
12704
12798
 
12705
12799
  static get observedAttributes() {
12706
- return ["value", "disabled", "choice-element", "drag", "overflow", "loop"];
12800
+ return ["value", "disabled", "choice-element", "drag", "overflow", "loop", "padding"];
12707
12801
  }
12708
12802
 
12709
12803
  get #overflowMode() {
@@ -13339,8 +13433,8 @@ class FigHandle extends HTMLElement {
13339
13433
  };
13340
13434
 
13341
13435
  const axes = this.#axes;
13342
- if (axes.x) this.style.left = `${resolve(xToken, rect.width, hw)}px`;
13343
- if (axes.y) this.style.top = `${resolve(yToken, rect.height, hh)}px`;
13436
+ if (axes.x) this.style.left = `${Math.round(resolve(xToken, rect.width, hw))}px`;
13437
+ if (axes.y) this.style.top = `${Math.round(resolve(yToken, rect.height, hh))}px`;
13344
13438
  }
13345
13439
 
13346
13440
  #syncValueAttribute() {
@@ -13370,7 +13464,7 @@ class FigHandle extends HTMLElement {
13370
13464
  select() {
13371
13465
  if (this.hasAttribute("disabled")) return;
13372
13466
  this.setAttribute("selected", "");
13373
- if (this.getAttribute("type") === "color") this.#showColorTip();
13467
+ if (this.getAttribute("type") === "color" && !this.#isDragging) this.#showColorTip();
13374
13468
  }
13375
13469
 
13376
13470
  deselect() {
@@ -13410,6 +13504,9 @@ class FigHandle extends HTMLElement {
13410
13504
  } else {
13411
13505
  this.style.setProperty("--fill", value);
13412
13506
  }
13507
+ if (this.#colorTip && value) {
13508
+ this.#colorTip.setAttribute("value", value);
13509
+ }
13413
13510
  }
13414
13511
  if (name === "drag") this.#syncDrag();
13415
13512
  if (name === "value" && !this.#applyingValue && !this.#isDragging) {
@@ -13491,11 +13588,11 @@ class FigHandle extends HTMLElement {
13491
13588
 
13492
13589
  if (axes.x) {
13493
13590
  const left = centerX * rect.width - handleW / 2;
13494
- this.style.left = `${Math.max(-handleW / 2, Math.min(rect.width - handleW / 2, left))}px`;
13591
+ this.style.left = `${Math.round(Math.max(-handleW / 2, Math.min(rect.width - handleW / 2, left)))}px`;
13495
13592
  }
13496
13593
  if (axes.y) {
13497
13594
  const top = centerY * rect.height - handleH / 2;
13498
- this.style.top = `${Math.max(-handleH / 2, Math.min(rect.height - handleH / 2, top))}px`;
13595
+ this.style.top = `${Math.round(Math.max(-handleH / 2, Math.min(rect.height - handleH / 2, top)))}px`;
13499
13596
  }
13500
13597
  };
13501
13598
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "3.15.0",
3
+ "version": "3.16.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",