@rogieking/figui3 3.17.0 → 3.19.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/README.md CHANGED
@@ -82,6 +82,7 @@ Minimal example:
82
82
  | [Easing Curve](#easing-curve) | `<fig-easing-curve>` | Bezier/spring curve editor |
83
83
  | [3D Rotate](#3d-rotate) | `<fig-3d-rotate>` | 3D cube rotation control |
84
84
  | [Handle](#handle) | `<fig-handle>` | Draggable handle on a surface |
85
+ | [Canvas Control](#canvas-control) | `<fig-canvas-control>` | Point with optional radius, angle, or second point |
85
86
  | [Dialog](#dialog) | `<fig-dialog>` | Modal/non-modal dialog |
86
87
  | [Popup](#popup) | `<fig-popup>` | Anchored floating surface |
87
88
  | [Toast](#toast) | `<fig-toast>` | Toast notification |
@@ -762,6 +763,8 @@ A draggable handle element. Positioned on a `drag-surface` container with axis c
762
763
  | `drag-snapping` | string | — | Snapping behavior |
763
764
  | `type` | string | — | `"color"` for color handle with `fig-color-tip` |
764
765
  | `control` | string | — | `"add"` or `"remove"` delegated to color tip |
766
+ | `hit-area` | string | — | Expanded interaction zone (unitless px). `"8"`, `"8 12"` (v h), or `"8 circle"` |
767
+ | `hit-area-mode` | string | `"handle"` | `"handle"` proxies to handle drag/select; `"delegate"` emits `hitareadown` event |
765
768
 
766
769
  **Events:**
767
770
 
@@ -771,6 +774,7 @@ A draggable handle element. Positioned on a `drag-surface` container with axis c
771
774
  | `change` | `{ x, y, px, py }` — on release |
772
775
  | `add` | — (when control="add") |
773
776
  | `remove` | — (when control="remove") |
777
+ | `hitareadown` | `{ originalEvent }` — when `hit-area-mode="delegate"` and the hit area is clicked |
774
778
 
775
779
  ```html
776
780
  <div style="position: relative; width: 200px; height: 200px; background: #eee;">
@@ -780,6 +784,54 @@ A draggable handle element. Positioned on a `drag-surface` container with axis c
780
784
 
781
785
  ---
782
786
 
787
+ #### Canvas Control
788
+
789
+ `<fig-canvas-control>` — [demo](https://rog.ie/figui3/#canvas-control)
790
+
791
+ A composite point control with optional radius circle, angle handle, or second point. Place inside a positioned container; the component uses `display: contents` and does not create its own box.
792
+
793
+ | Attribute | Type | Default | Description |
794
+ |---|---|---|---|
795
+ | `type` | string | `"point"` | `"point"`, `"color"`, `"point-radius"`, `"point-radius-angle"`, `"point-point"` |
796
+ | `value` | JSON string | — | `{ "x": 50, "y": 50 }` — see type-specific shapes below |
797
+ | `name` | string | — | Tooltip label(s). Comma-separated for two handles: `"Start, End"` |
798
+ | `color` | string | — | Passthrough color for `type="color"` handle |
799
+ | `tooltips` | string | `"true"` | Show value tooltips on interaction |
800
+ | `disabled` | boolean | `false` | Disable all interaction |
801
+ | `drag-surface` | string | `"parent"` | Forwarded to inner `fig-handle`s |
802
+ | `snapping` | string | `"false"` | `"false"`, `"true"`, `"modifier"` — applies to all handles |
803
+
804
+ **Value shapes by type:**
805
+
806
+ | Type | Value shape |
807
+ |---|---|
808
+ | `point`, `color` | `{ x, y }` |
809
+ | `point-radius` | `{ x, y, radius }` — radius: number (px) or `"25%"` |
810
+ | `point-radius-angle` | `{ x, y, radius, angle }` — angle in degrees |
811
+ | `point-point` | `{ x, y, x2, y2 }` — angle and length inferred |
812
+
813
+ **Events:**
814
+
815
+ | Event | Detail |
816
+ |---|---|
817
+ | `input` | Value object (shape depends on type) — while dragging |
818
+ | `change` | Value object (shape depends on type) — on release |
819
+
820
+ For `point-point`, both handles support direct drag (with a dynamic directional resize cursor) and rotation via their hit area (dragging from the hit area rotates around the opposite handle at fixed distance, with a rotate cursor).
821
+
822
+ ```html
823
+ <div style="position: relative; width: 200px; height: 200px; background: #eee;">
824
+ <fig-canvas-control
825
+ type="point-point"
826
+ name="Start, End"
827
+ value='{"x":25,"y":25,"x2":75,"y2":75}'
828
+ snapping="modifier"
829
+ ></fig-canvas-control>
830
+ </div>
831
+ ```
832
+
833
+ ---
834
+
783
835
  ### Layout & Feedback
784
836
 
785
837
  #### Field
package/components.css CHANGED
@@ -3125,7 +3125,11 @@ fig-input-fill {
3125
3125
  }
3126
3126
  }
3127
3127
  fig-input-gradient {
3128
+ --height: 1.5rem;
3128
3129
  position: relative;
3130
+ height: var(--height);
3131
+ width: 100%;
3132
+ min-width: 0;
3129
3133
 
3130
3134
  &:focus,
3131
3135
  &:active,
@@ -3147,6 +3151,7 @@ fig-input-gradient {
3147
3151
  align-items: center;
3148
3152
  inset: 0;
3149
3153
  pointer-events: auto;
3154
+ z-index: 1;
3150
3155
 
3151
3156
  fig-handle {
3152
3157
  pointer-events: auto;
@@ -3242,7 +3247,6 @@ fig-input-palette {
3242
3247
  border-radius: var(--radius-medium) !important;
3243
3248
  }
3244
3249
  }
3245
-
3246
3250
  }
3247
3251
 
3248
3252
  fig-field[direction="horizontal"]:has(> fig-input-palette) {
@@ -4811,20 +4815,33 @@ fig-handle {
4811
4815
  --box-shadow: 0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--elevation-100-canvas);
4812
4816
  --outline: none;
4813
4817
  --border: none;
4818
+ --fig-handle-hit-area-opacity: 0;
4819
+ --fig-handle-hit-area-size: 0;
4814
4820
 
4815
- display: grid;
4816
4821
  margin: 0;
4817
4822
  padding: 0;
4818
4823
  place-items: center;
4819
4824
  width: var(--width);
4820
4825
  height: var(--height);
4821
- background: var(--handle-color);
4822
4826
  border-radius: var(--border-radius);
4823
- box-shadow: var(--box-shadow);
4824
- outline: var(--outline);
4825
- border: var(--border);
4827
+ display: grid;
4826
4828
 
4827
4829
  &::before {
4830
+ content: "";
4831
+ color-scheme: light only;
4832
+ width: var(--width);
4833
+ height: var(--height);
4834
+ background: var(--handle-color);
4835
+ border-radius: var(--border-radius);
4836
+ box-shadow: var(--box-shadow);
4837
+ outline: var(--outline);
4838
+ border: var(--border);
4839
+ place-self: center;
4840
+ grid-area: 1 / 1;
4841
+ z-index: 1;
4842
+ }
4843
+
4844
+ &::after {
4828
4845
  content: "";
4829
4846
  color-scheme: light only;
4830
4847
  width: calc(var(--width) - var(--ring-width) * 2);
@@ -4833,6 +4850,8 @@ fig-handle {
4833
4850
  border-radius: var(--border-radius);
4834
4851
  box-shadow: inset 0 0 0 1px var(--figma-color-bordertranslucent);
4835
4852
  place-self: center;
4853
+ grid-area: 1 / 1;
4854
+ z-index: 2;
4836
4855
  }
4837
4856
 
4838
4857
  &[size="small"] {
@@ -4868,6 +4887,15 @@ fig-handle {
4868
4887
  z-index: 10;
4869
4888
  }
4870
4889
  }
4890
+
4891
+ .fig-handle-hit-area {
4892
+ position: absolute;
4893
+ inset: calc(var(--fig-handle-hit-area-size) * -0.5px);
4894
+ pointer-events: auto;
4895
+ background: red;
4896
+ opacity: var(--fig-handle-hit-area-opacity, 0);
4897
+ z-index: 0;
4898
+ }
4871
4899
  }
4872
4900
 
4873
4901
  fig-color-tip {
@@ -4949,3 +4977,74 @@ fig-color-tip {
4949
4977
  }
4950
4978
  }
4951
4979
  }
4980
+
4981
+ /* Canvas Control */
4982
+ fig-canvas-control {
4983
+ display: contents;
4984
+ --fig-canvas-control-line-stroke: rgba(255, 255, 255, 0.5);
4985
+ --fig-canvas-control-line-stroke-hover: var(--figma-color-bg-brand);
4986
+ --fig-canvas-control-line-stroke-active: var(--figma-color-bg-brand);
4987
+ --fig-canvas-control-line-stroke-width: 1.5px;
4988
+
4989
+ & > fig-handle,
4990
+ & > fig-tooltip:has(fig-handle:not([size="small"])) {
4991
+ z-index: 1;
4992
+ }
4993
+
4994
+ .fig-canvas-control-radius {
4995
+ position: absolute;
4996
+ pointer-events: none;
4997
+ overflow: visible;
4998
+
4999
+ circle {
5000
+ fill: none;
5001
+ stroke: var(--fig-canvas-control-line-stroke);
5002
+ stroke-width: var(--fig-canvas-control-line-stroke-width);
5003
+ paint-order: stroke fill;
5004
+ filter: drop-shadow(0 0 0.5px rgba(0, 0, 0, 0.3))
5005
+ drop-shadow(0 0.5px 1px rgba(0, 0, 0, 0.12));
5006
+ }
5007
+
5008
+ .fig-canvas-control-radius-hit {
5009
+ stroke: transparent;
5010
+ stroke-width: 12px;
5011
+ pointer-events: stroke;
5012
+ filter: none;
5013
+
5014
+ &:hover ~ circle {
5015
+ stroke: var(--fig-canvas-control-line-stroke-hover);
5016
+ }
5017
+ }
5018
+ }
5019
+
5020
+ &:has(.fig-canvas-control-radius-hit:hover) .fig-canvas-control-angle-line,
5021
+ &.fig-canvas-control-ring-active .fig-canvas-control-angle-line {
5022
+ stroke: var(--fig-canvas-control-line-stroke-active);
5023
+ }
5024
+
5025
+ &.fig-canvas-control-ring-active
5026
+ .fig-canvas-control-radius
5027
+ circle:not(.fig-canvas-control-radius-hit) {
5028
+ stroke: var(--fig-canvas-control-line-stroke-active);
5029
+ }
5030
+
5031
+ .fig-canvas-control-angle-svg {
5032
+ pointer-events: none;
5033
+ }
5034
+
5035
+ .fig-canvas-control-angle-line {
5036
+ stroke: var(--fig-canvas-control-line-stroke);
5037
+ stroke-width: var(--fig-canvas-control-line-stroke-width);
5038
+ paint-order: stroke fill;
5039
+ filter: drop-shadow(0 0 0.5px rgba(0, 0, 0, 0.3))
5040
+ drop-shadow(0 0.5px 1px rgba(0, 0, 0, 0.12));
5041
+ }
5042
+
5043
+ fig-handle[size="small"] {
5044
+ z-index: 2;
5045
+
5046
+ .fig-handle-hit-area {
5047
+ cursor: grab;
5048
+ }
5049
+ }
5050
+ }