@rogieking/figui3 6.10.0 → 6.11.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/components.css +6 -0
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +22 -18
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +20 -20
- package/dist/fig.css +1 -1
- package/dist/fig.js +17 -17
- package/fig-editor.css +0 -12
- package/fig-editor.js +26 -22
- package/fig-lab.css +76 -1
- package/fig-lab.js +451 -2
- package/fig.js +20 -3
- package/package.json +1 -1
package/fig-editor.css
CHANGED
|
@@ -78,12 +78,6 @@ fig-fill-picker {
|
|
|
78
78
|
width: 240px;
|
|
79
79
|
padding: 0;
|
|
80
80
|
|
|
81
|
-
& > fig-header {
|
|
82
|
-
padding: var(--spacer-2);
|
|
83
|
-
min-width: 0;
|
|
84
|
-
overflow: hidden;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
81
|
fig-tab {
|
|
88
82
|
font-size: 0.6875rem;
|
|
89
83
|
padding: var(--spacer-1) var(--spacer-1);
|
|
@@ -91,12 +85,6 @@ fig-fill-picker {
|
|
|
91
85
|
& > fig-content {
|
|
92
86
|
padding-bottom: var(--spacer-2-5);
|
|
93
87
|
}
|
|
94
|
-
.fig-fill-picker-type-label {
|
|
95
|
-
font-size: var(--font-size-small);
|
|
96
|
-
font-weight: var(--font-weight-medium);
|
|
97
|
-
color: var(--figma-color-text);
|
|
98
|
-
padding: var(--spacer-1) var(--spacer-2);
|
|
99
|
-
}
|
|
100
88
|
|
|
101
89
|
.fig-fill-picker-gamut {
|
|
102
90
|
margin-left: auto;
|
package/fig-editor.js
CHANGED
|
@@ -1439,28 +1439,6 @@ class FigFillPicker extends HTMLElement {
|
|
|
1439
1439
|
<fig-preview class="fig-fill-picker-gradient-preview">
|
|
1440
1440
|
<fig-input-gradient class="fig-fill-picker-gradient-bar-input" aria-label="Gradient stops" edit="true" mode="tip" size="large" value='${JSON.stringify({ type: "gradient", gradient: gradientToValueShape(this.#gradient) })}'></fig-input-gradient>
|
|
1441
1441
|
</fig-preview>
|
|
1442
|
-
<fig-field class="fig-fill-picker-gradient-interpolation">
|
|
1443
|
-
<label>Mixing</label>
|
|
1444
|
-
<fig-dropdown class="fig-fill-picker-gradient-space" label="Gradient mixing" full ${expAttr} value="${
|
|
1445
|
-
this.#gradient.interpolationSpace === "oklch"
|
|
1446
|
-
? `oklch-${this.#gradient.hueInterpolation || "shorter"}`
|
|
1447
|
-
: this.#gradient.interpolationSpace
|
|
1448
|
-
}">
|
|
1449
|
-
<optgroup label="sRGB">
|
|
1450
|
-
<option value="srgb">Classic</option>
|
|
1451
|
-
<option value="srgb-linear">Linear</option>
|
|
1452
|
-
</optgroup>
|
|
1453
|
-
<optgroup label="OKLab">
|
|
1454
|
-
<option value="oklab">Perceptual</option>
|
|
1455
|
-
</optgroup>
|
|
1456
|
-
<optgroup label="OKLCH">
|
|
1457
|
-
<option value="oklch-shorter">Shorter hue</option>
|
|
1458
|
-
<option value="oklch-longer">Longer hue</option>
|
|
1459
|
-
<option value="oklch-increasing">Increasing hue</option>
|
|
1460
|
-
<option value="oklch-decreasing">Decreasing hue</option>
|
|
1461
|
-
</optgroup>
|
|
1462
|
-
</fig-dropdown>
|
|
1463
|
-
</fig-field>
|
|
1464
1442
|
<div class="fig-fill-picker-gradient-stops">
|
|
1465
1443
|
<fig-header class="fig-fill-picker-gradient-stops-header" borderless>
|
|
1466
1444
|
<span>Stops</span>
|
|
@@ -1470,6 +1448,32 @@ class FigFillPicker extends HTMLElement {
|
|
|
1470
1448
|
</fig-header>
|
|
1471
1449
|
<div class="fig-fill-picker-gradient-stops-list"></div>
|
|
1472
1450
|
</div>
|
|
1451
|
+
<div class="fig-fill-picker-gradient-interpolation">
|
|
1452
|
+
<fig-header class="fig-fill-picker-gradient-interpolation-header" borderless>
|
|
1453
|
+
<span>Color interpolation</span>
|
|
1454
|
+
</fig-header>
|
|
1455
|
+
<fig-field class="fig-fill-picker-gradient-interpolation-field">
|
|
1456
|
+
<fig-dropdown class="fig-fill-picker-gradient-space" label="Color interpolation" full ${expAttr} value="${
|
|
1457
|
+
this.#gradient.interpolationSpace === "oklch"
|
|
1458
|
+
? `oklch-${this.#gradient.hueInterpolation || "shorter"}`
|
|
1459
|
+
: this.#gradient.interpolationSpace
|
|
1460
|
+
}">
|
|
1461
|
+
<optgroup label="sRGB">
|
|
1462
|
+
<option value="srgb">Classic</option>
|
|
1463
|
+
<option value="srgb-linear">Linear</option>
|
|
1464
|
+
</optgroup>
|
|
1465
|
+
<optgroup label="OKLab">
|
|
1466
|
+
<option value="oklab">Perceptual</option>
|
|
1467
|
+
</optgroup>
|
|
1468
|
+
<optgroup label="OKLCH">
|
|
1469
|
+
<option value="oklch-shorter">Shorter hue</option>
|
|
1470
|
+
<option value="oklch-longer">Longer hue</option>
|
|
1471
|
+
<option value="oklch-increasing">Increasing hue</option>
|
|
1472
|
+
<option value="oklch-decreasing">Decreasing hue</option>
|
|
1473
|
+
</optgroup>
|
|
1474
|
+
</fig-dropdown>
|
|
1475
|
+
</fig-field>
|
|
1476
|
+
</div>
|
|
1473
1477
|
`;
|
|
1474
1478
|
|
|
1475
1479
|
this.#updateGradientUI();
|
package/fig-lab.css
CHANGED
|
@@ -302,7 +302,7 @@ fig-field-slider {
|
|
|
302
302
|
background-color: transparent;
|
|
303
303
|
box-shadow: none !important;
|
|
304
304
|
position: absolute;
|
|
305
|
-
right:
|
|
305
|
+
right: 0;
|
|
306
306
|
width: fit-content;
|
|
307
307
|
min-width: fit-content;
|
|
308
308
|
max-width: max-content;
|
|
@@ -636,6 +636,32 @@ fig-input-oscillator {
|
|
|
636
636
|
}
|
|
637
637
|
}
|
|
638
638
|
|
|
639
|
+
.fig-input-oscillator-frequency-handle {
|
|
640
|
+
cursor: ew-resize;
|
|
641
|
+
|
|
642
|
+
fig-handle,
|
|
643
|
+
fig-handle:hover,
|
|
644
|
+
fig-handle .fig-handle-hit-area,
|
|
645
|
+
fig-handle:hover .fig-handle-hit-area,
|
|
646
|
+
fig-handle:is(:active, .active, .dragging),
|
|
647
|
+
fig-handle:is(:active, .active, .dragging) .fig-handle-hit-area {
|
|
648
|
+
cursor: ew-resize !important;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.fig-input-oscillator-amplitude-handle {
|
|
653
|
+
cursor: ns-resize;
|
|
654
|
+
|
|
655
|
+
fig-handle,
|
|
656
|
+
fig-handle:hover,
|
|
657
|
+
fig-handle .fig-handle-hit-area,
|
|
658
|
+
fig-handle:hover .fig-handle-hit-area,
|
|
659
|
+
fig-handle:is(:active, .active, .dragging),
|
|
660
|
+
fig-handle:is(:active, .active, .dragging) .fig-handle-hit-area {
|
|
661
|
+
cursor: ns-resize !important;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
639
665
|
.fig-input-oscillator-waves {
|
|
640
666
|
display: flex;
|
|
641
667
|
flex-direction: column;
|
|
@@ -673,3 +699,52 @@ fig-input-oscillator {
|
|
|
673
699
|
}
|
|
674
700
|
}
|
|
675
701
|
|
|
702
|
+
/* Reorder */
|
|
703
|
+
fig-reorder {
|
|
704
|
+
display: contents;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
fig-reorder:not([handle]):not([disabled]):not([disabled="false"]) > [data-reorder-item] {
|
|
708
|
+
cursor: grab;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
fig-reorder:not([handle]):not([disabled]):not([disabled="false"]) > [data-reorder-item] > fig-header,
|
|
712
|
+
fig-reorder:not([handle]):not([disabled]):not([disabled="false"]) > [data-reorder-item] > fig-header *,
|
|
713
|
+
fig-reorder:not([handle]):not([disabled]):not([disabled="false"]) > [data-reorder-item] > label {
|
|
714
|
+
cursor: grab;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
fig-reorder > .dragging {
|
|
718
|
+
opacity: 0.6;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
[data-reorder-handle] {
|
|
722
|
+
cursor: grab;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
fig-reorder[disabled]:not([disabled="false"]) [data-reorder-handle] {
|
|
726
|
+
cursor: inherit;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
body.fig-reorder-dragging,
|
|
730
|
+
body.fig-reorder-dragging * {
|
|
731
|
+
user-select: none !important;
|
|
732
|
+
-webkit-user-select: none !important;
|
|
733
|
+
cursor: grabbing !important;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.fig-reorder-indicator {
|
|
737
|
+
position: fixed;
|
|
738
|
+
pointer-events: none;
|
|
739
|
+
z-index: 10000;
|
|
740
|
+
background: var(--figma-color-border-strong);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.fig-reorder-indicator[data-axis="vertical"] {
|
|
744
|
+
height: 2px;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.fig-reorder-indicator[data-axis="horizontal"] {
|
|
748
|
+
width: 2px;
|
|
749
|
+
}
|
|
750
|
+
|
package/fig-lab.js
CHANGED
|
@@ -2098,8 +2098,8 @@ class FigInputOscillator extends HTMLElement {
|
|
|
2098
2098
|
<line class="fig-input-oscillator-baseline"></line>
|
|
2099
2099
|
<path class="fig-input-oscillator-path"></path>
|
|
2100
2100
|
<circle class="fig-input-oscillator-playhead"></circle>
|
|
2101
|
-
<foreignObject class="fig-input-oscillator-handle fig-input-oscillator-amplitude-handle" data-handle="amplitude" width="20" height="20"><div class="fig-input-oscillator-handle-inner"><fig-tooltip text="Amplitude"><fig-handle
|
|
2102
|
-
<foreignObject class="fig-input-oscillator-handle fig-input-oscillator-frequency-handle" data-handle="frequency" width="20" height="20"><div class="fig-input-oscillator-handle-inner"><fig-tooltip text="Frequency"><fig-handle
|
|
2101
|
+
<foreignObject class="fig-input-oscillator-handle fig-input-oscillator-amplitude-handle" data-handle="amplitude" width="20" height="20"><div class="fig-input-oscillator-handle-inner"><fig-tooltip text="Amplitude"><fig-handle size="small" aria-label="Oscillator amplitude handle"${disabled}></fig-handle></fig-tooltip></div></foreignObject>
|
|
2102
|
+
<foreignObject class="fig-input-oscillator-handle fig-input-oscillator-frequency-handle" data-handle="frequency" width="20" height="20"><div class="fig-input-oscillator-handle-inner"><fig-tooltip text="Frequency"><fig-handle size="small" aria-label="Oscillator frequency handle"${disabled}></fig-handle></fig-tooltip></div></foreignObject>
|
|
2103
2103
|
</svg>
|
|
2104
2104
|
</div>
|
|
2105
2105
|
${this.#isEditEnabled() ? this.#getWaveControlsHTML(disabled) : ""}`;
|
|
@@ -2589,6 +2589,13 @@ class FigInputOscillator extends HTMLElement {
|
|
|
2589
2589
|
|
|
2590
2590
|
#startDrag(event, type) {
|
|
2591
2591
|
this.#isDragging = type;
|
|
2592
|
+
this.#svg?.classList.add("dragging");
|
|
2593
|
+
const dragCursor =
|
|
2594
|
+
type === "frequency" ? "ew-resize" : type === "amplitude" ? "ns-resize" : "";
|
|
2595
|
+
const prevBodyCursor = document.body.style.cursor;
|
|
2596
|
+
if (dragCursor) {
|
|
2597
|
+
document.body.style.cursor = dragCursor;
|
|
2598
|
+
}
|
|
2592
2599
|
|
|
2593
2600
|
const onMove = (moveEvent) => {
|
|
2594
2601
|
if (!this.#isDragging) return;
|
|
@@ -2623,6 +2630,10 @@ class FigInputOscillator extends HTMLElement {
|
|
|
2623
2630
|
|
|
2624
2631
|
const onUp = () => {
|
|
2625
2632
|
this.#isDragging = null;
|
|
2633
|
+
this.#svg?.classList.remove("dragging");
|
|
2634
|
+
if (dragCursor) {
|
|
2635
|
+
document.body.style.cursor = prevBodyCursor;
|
|
2636
|
+
}
|
|
2626
2637
|
document.removeEventListener("pointermove", onMove);
|
|
2627
2638
|
document.removeEventListener("pointerup", onUp);
|
|
2628
2639
|
this.#emit("change");
|
|
@@ -3221,3 +3232,441 @@ class FigInputAngle extends HTMLElement {
|
|
|
3221
3232
|
}
|
|
3222
3233
|
customElements.define("fig-input-angle", FigInputAngle);
|
|
3223
3234
|
|
|
3235
|
+
/* Reorder wrapper */
|
|
3236
|
+
class FigReorder extends HTMLElement {
|
|
3237
|
+
static observedAttributes = ["axis", "handle", "disabled"];
|
|
3238
|
+
|
|
3239
|
+
static #DRAG_THRESHOLD = 6;
|
|
3240
|
+
|
|
3241
|
+
static #INTERACTIVE_SELECTORS = [
|
|
3242
|
+
"input",
|
|
3243
|
+
"button",
|
|
3244
|
+
"select",
|
|
3245
|
+
"textarea",
|
|
3246
|
+
"a[href]",
|
|
3247
|
+
"label",
|
|
3248
|
+
"summary",
|
|
3249
|
+
"fig-button",
|
|
3250
|
+
"fig-dropdown",
|
|
3251
|
+
"fig-slider",
|
|
3252
|
+
"fig-input-number",
|
|
3253
|
+
"fig-input-text",
|
|
3254
|
+
"fig-field-slider",
|
|
3255
|
+
"fig-checkbox",
|
|
3256
|
+
"fig-switch",
|
|
3257
|
+
"fig-combo-input",
|
|
3258
|
+
"fig-segmented-control",
|
|
3259
|
+
"fig-input-color",
|
|
3260
|
+
"fig-input-fill",
|
|
3261
|
+
'[contenteditable="true"]',
|
|
3262
|
+
];
|
|
3263
|
+
|
|
3264
|
+
#childObserver = null;
|
|
3265
|
+
#bindings = new Map();
|
|
3266
|
+
#drag = null;
|
|
3267
|
+
#indicator = null;
|
|
3268
|
+
|
|
3269
|
+
connectedCallback() {
|
|
3270
|
+
this.style.display = "contents";
|
|
3271
|
+
this.#syncChildren();
|
|
3272
|
+
this.#childObserver = new MutationObserver(() => this.#syncChildren());
|
|
3273
|
+
this.#childObserver.observe(this, { childList: true });
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
disconnectedCallback() {
|
|
3277
|
+
this.#childObserver?.disconnect();
|
|
3278
|
+
this.#childObserver = null;
|
|
3279
|
+
this.#unbindAll();
|
|
3280
|
+
this.#cancelDrag();
|
|
3281
|
+
this.#removeIndicator();
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
attributeChangedCallback() {
|
|
3285
|
+
if (this.isConnected) this.#syncChildren();
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
get #disabled() {
|
|
3289
|
+
return (
|
|
3290
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false"
|
|
3291
|
+
);
|
|
3292
|
+
}
|
|
3293
|
+
|
|
3294
|
+
get #axis() {
|
|
3295
|
+
const value = (this.getAttribute("axis") || "vertical").trim().toLowerCase();
|
|
3296
|
+
return value === "horizontal" ? "horizontal" : "vertical";
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
get #handleSelector() {
|
|
3300
|
+
return (this.getAttribute("handle") || "").trim();
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
#getElementChildren() {
|
|
3304
|
+
return [...this.children].filter((node) => node.nodeType === Node.ELEMENT_NODE);
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
#syncChildren() {
|
|
3308
|
+
const children = this.#getElementChildren();
|
|
3309
|
+
const childSet = new Set(children);
|
|
3310
|
+
|
|
3311
|
+
for (const [child, binding] of this.#bindings) {
|
|
3312
|
+
if (!childSet.has(child)) {
|
|
3313
|
+
binding.target.removeEventListener(
|
|
3314
|
+
"pointerdown",
|
|
3315
|
+
binding.onPointerDown,
|
|
3316
|
+
true,
|
|
3317
|
+
);
|
|
3318
|
+
this.#bindings.delete(child);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
this.#clearHandleMarks(children);
|
|
3323
|
+
this.#clearReorderItemMarks(children);
|
|
3324
|
+
|
|
3325
|
+
if (this.#disabled || children.length < 2) {
|
|
3326
|
+
this.#unbindAll();
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
for (const child of children) {
|
|
3331
|
+
const target = this.#getDragTarget(child);
|
|
3332
|
+
const existing = this.#bindings.get(child);
|
|
3333
|
+
if (existing && existing.target === target) continue;
|
|
3334
|
+
if (existing) {
|
|
3335
|
+
existing.target.removeEventListener(
|
|
3336
|
+
"pointerdown",
|
|
3337
|
+
existing.onPointerDown,
|
|
3338
|
+
true,
|
|
3339
|
+
);
|
|
3340
|
+
this.#bindings.delete(child);
|
|
3341
|
+
}
|
|
3342
|
+
if (!target) continue;
|
|
3343
|
+
this.#bindChild(child, target);
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
this.#markHandles(children);
|
|
3347
|
+
this.#markReorderItems(children);
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
#clearReorderItemMarks(children) {
|
|
3351
|
+
for (const child of children) {
|
|
3352
|
+
child.removeAttribute("data-reorder-item");
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
#markReorderItems(children) {
|
|
3357
|
+
for (const child of children) {
|
|
3358
|
+
child.setAttribute("data-reorder-item", "");
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
#clearHandleMarks(children) {
|
|
3363
|
+
for (const child of children) {
|
|
3364
|
+
child
|
|
3365
|
+
.querySelectorAll("[data-reorder-handle]")
|
|
3366
|
+
.forEach((node) => node.removeAttribute("data-reorder-handle"));
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
#markHandles(children) {
|
|
3371
|
+
if (!this.#handleSelector) return;
|
|
3372
|
+
for (const child of children) {
|
|
3373
|
+
const handle = child.querySelector(this.#handleSelector);
|
|
3374
|
+
if (handle) handle.setAttribute("data-reorder-handle", "");
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
#getDragTarget(child) {
|
|
3379
|
+
if (this.#handleSelector) {
|
|
3380
|
+
return child.querySelector(this.#handleSelector);
|
|
3381
|
+
}
|
|
3382
|
+
return child;
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
#bindChild(child, target) {
|
|
3386
|
+
const onPointerDown = (event) => {
|
|
3387
|
+
if (this.#disabled || this.#getElementChildren().length < 2) return;
|
|
3388
|
+
if (event.button !== 0) return;
|
|
3389
|
+
if (
|
|
3390
|
+
!this.#handleSelector &&
|
|
3391
|
+
this.#isInteractiveTarget(event.target, child)
|
|
3392
|
+
) {
|
|
3393
|
+
return;
|
|
3394
|
+
}
|
|
3395
|
+
this.#startPendingDrag(event, child, target);
|
|
3396
|
+
};
|
|
3397
|
+
|
|
3398
|
+
target.addEventListener("pointerdown", onPointerDown, true);
|
|
3399
|
+
this.#bindings.set(child, { target, onPointerDown });
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
#isInteractiveTarget(target, child) {
|
|
3403
|
+
let node = target;
|
|
3404
|
+
while (node && node !== child) {
|
|
3405
|
+
if (node instanceof Element) {
|
|
3406
|
+
for (const selector of FigReorder.#INTERACTIVE_SELECTORS) {
|
|
3407
|
+
if (node.matches(selector)) return true;
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
node = node.parentElement;
|
|
3411
|
+
}
|
|
3412
|
+
return false;
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
static #DRAGGING_BODY_CLASS = "fig-reorder-dragging";
|
|
3416
|
+
|
|
3417
|
+
static #setDocumentDragging(active) {
|
|
3418
|
+
document.body.classList.toggle(FigReorder.#DRAGGING_BODY_CLASS, active);
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
#startPendingDrag(event, item, target) {
|
|
3422
|
+
this.#cancelDrag();
|
|
3423
|
+
|
|
3424
|
+
const state = {
|
|
3425
|
+
item,
|
|
3426
|
+
target,
|
|
3427
|
+
pointerId: event.pointerId,
|
|
3428
|
+
oldIndex: this.#getElementChildren().indexOf(item),
|
|
3429
|
+
startX: event.clientX,
|
|
3430
|
+
startY: event.clientY,
|
|
3431
|
+
active: false,
|
|
3432
|
+
onMove: null,
|
|
3433
|
+
onUp: null,
|
|
3434
|
+
onKeyDown: null,
|
|
3435
|
+
};
|
|
3436
|
+
|
|
3437
|
+
state.onMove = (moveEvent) => {
|
|
3438
|
+
if (moveEvent.pointerId !== state.pointerId) return;
|
|
3439
|
+
|
|
3440
|
+
const dx = moveEvent.clientX - state.startX;
|
|
3441
|
+
const dy = moveEvent.clientY - state.startY;
|
|
3442
|
+
|
|
3443
|
+
if (!state.active) {
|
|
3444
|
+
if (dx * dx + dy * dy < FigReorder.#DRAG_THRESHOLD * FigReorder.#DRAG_THRESHOLD) {
|
|
3445
|
+
return;
|
|
3446
|
+
}
|
|
3447
|
+
state.active = true;
|
|
3448
|
+
event.preventDefault();
|
|
3449
|
+
event.stopPropagation();
|
|
3450
|
+
item.classList.add("dragging");
|
|
3451
|
+
FigReorder.#setDocumentDragging(true);
|
|
3452
|
+
try {
|
|
3453
|
+
target.setPointerCapture(state.pointerId);
|
|
3454
|
+
} catch {}
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
moveEvent.preventDefault();
|
|
3458
|
+
const pointer =
|
|
3459
|
+
this.#axis === "horizontal" ? moveEvent.clientX : moveEvent.clientY;
|
|
3460
|
+
const index = this.#getInsertIndex(pointer);
|
|
3461
|
+
this.#moveItemToIndex(item, index);
|
|
3462
|
+
this.#updateIndicator(index, item);
|
|
3463
|
+
};
|
|
3464
|
+
|
|
3465
|
+
state.onKeyDown = (keyEvent) => {
|
|
3466
|
+
if (keyEvent.key !== "Escape" || !state.active) return;
|
|
3467
|
+
keyEvent.preventDefault();
|
|
3468
|
+
this.#finishDrag(state, true);
|
|
3469
|
+
};
|
|
3470
|
+
|
|
3471
|
+
state.onUp = (upEvent) => {
|
|
3472
|
+
if (upEvent.pointerId !== state.pointerId) return;
|
|
3473
|
+
this.#finishDrag(state, false);
|
|
3474
|
+
};
|
|
3475
|
+
|
|
3476
|
+
this.#drag = state;
|
|
3477
|
+
window.addEventListener("pointermove", state.onMove);
|
|
3478
|
+
window.addEventListener("pointerup", state.onUp);
|
|
3479
|
+
window.addEventListener("pointercancel", state.onUp);
|
|
3480
|
+
window.addEventListener("keydown", state.onKeyDown);
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
#getInsertIndex(pointer) {
|
|
3484
|
+
const items = this.#getElementChildren();
|
|
3485
|
+
const horizontal = this.#axis === "horizontal";
|
|
3486
|
+
|
|
3487
|
+
for (let i = 0; i < items.length; i++) {
|
|
3488
|
+
const rect = items[i].getBoundingClientRect();
|
|
3489
|
+
const midpoint = horizontal
|
|
3490
|
+
? rect.left + rect.width / 2
|
|
3491
|
+
: rect.top + rect.height / 2;
|
|
3492
|
+
if (pointer < midpoint) return i;
|
|
3493
|
+
}
|
|
3494
|
+
|
|
3495
|
+
return items.length;
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
#shouldShowIndicator(item, index) {
|
|
3499
|
+
const items = this.#getElementChildren();
|
|
3500
|
+
const currentIndex = items.indexOf(item);
|
|
3501
|
+
if (currentIndex === -1) return false;
|
|
3502
|
+
|
|
3503
|
+
const clamped = Math.max(0, Math.min(index, items.length));
|
|
3504
|
+
|
|
3505
|
+
// Hide only the redundant bottom line when the dragged item is already last.
|
|
3506
|
+
if (clamped >= items.length && currentIndex === items.length - 1) {
|
|
3507
|
+
return false;
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
return true;
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
#getReorderBounds(items) {
|
|
3514
|
+
if (!items.length) return null;
|
|
3515
|
+
|
|
3516
|
+
let left = Infinity;
|
|
3517
|
+
let right = -Infinity;
|
|
3518
|
+
let top = Infinity;
|
|
3519
|
+
let bottom = -Infinity;
|
|
3520
|
+
|
|
3521
|
+
for (const item of items) {
|
|
3522
|
+
const rect = item.getBoundingClientRect();
|
|
3523
|
+
left = Math.min(left, rect.left);
|
|
3524
|
+
right = Math.max(right, rect.right);
|
|
3525
|
+
top = Math.min(top, rect.top);
|
|
3526
|
+
bottom = Math.max(bottom, rect.bottom);
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3529
|
+
return {
|
|
3530
|
+
left,
|
|
3531
|
+
top,
|
|
3532
|
+
width: right - left,
|
|
3533
|
+
height: bottom - top,
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
#ensureIndicator() {
|
|
3538
|
+
if (this.#indicator) return this.#indicator;
|
|
3539
|
+
|
|
3540
|
+
const indicator = document.createElement("div");
|
|
3541
|
+
indicator.className = "fig-reorder-indicator";
|
|
3542
|
+
indicator.setAttribute("data-axis", this.#axis);
|
|
3543
|
+
document.body.appendChild(indicator);
|
|
3544
|
+
this.#indicator = indicator;
|
|
3545
|
+
return indicator;
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
#updateIndicator(index, item) {
|
|
3549
|
+
const items = this.#getElementChildren();
|
|
3550
|
+
if (!this.#shouldShowIndicator(item, index)) {
|
|
3551
|
+
this.#removeIndicator();
|
|
3552
|
+
return;
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
const bounds = this.#getReorderBounds(items);
|
|
3556
|
+
if (!bounds) {
|
|
3557
|
+
this.#removeIndicator();
|
|
3558
|
+
return;
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
const indicator = this.#ensureIndicator();
|
|
3562
|
+
indicator.setAttribute("data-axis", this.#axis);
|
|
3563
|
+
|
|
3564
|
+
if (this.#axis === "horizontal") {
|
|
3565
|
+
let x;
|
|
3566
|
+
if (index <= 0) {
|
|
3567
|
+
x = items[0].getBoundingClientRect().left;
|
|
3568
|
+
} else if (index >= items.length) {
|
|
3569
|
+
x = items[items.length - 1].getBoundingClientRect().right;
|
|
3570
|
+
} else {
|
|
3571
|
+
x = items[index].getBoundingClientRect().left;
|
|
3572
|
+
}
|
|
3573
|
+
|
|
3574
|
+
indicator.style.left = `${x - 1}px`;
|
|
3575
|
+
indicator.style.top = `${bounds.top}px`;
|
|
3576
|
+
indicator.style.width = "2px";
|
|
3577
|
+
indicator.style.height = `${bounds.height}px`;
|
|
3578
|
+
return;
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
let y;
|
|
3582
|
+
if (index <= 0) {
|
|
3583
|
+
y = items[0].getBoundingClientRect().top;
|
|
3584
|
+
} else if (index >= items.length) {
|
|
3585
|
+
y = items[items.length - 1].getBoundingClientRect().bottom;
|
|
3586
|
+
} else {
|
|
3587
|
+
y = items[index].getBoundingClientRect().top;
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
indicator.style.left = `${bounds.left}px`;
|
|
3591
|
+
indicator.style.top = `${y - 1}px`;
|
|
3592
|
+
indicator.style.width = `${bounds.width}px`;
|
|
3593
|
+
indicator.style.height = "2px";
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
#removeIndicator() {
|
|
3597
|
+
this.#indicator?.remove();
|
|
3598
|
+
this.#indicator = null;
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
#moveItemToIndex(item, index) {
|
|
3602
|
+
const items = this.#getElementChildren();
|
|
3603
|
+
const clamped = Math.max(0, Math.min(index, items.length));
|
|
3604
|
+
|
|
3605
|
+
if (clamped >= items.length) {
|
|
3606
|
+
if (items[items.length - 1] !== item) this.appendChild(item);
|
|
3607
|
+
return;
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3610
|
+
const ref = items[clamped];
|
|
3611
|
+
if (ref !== item) this.insertBefore(item, ref);
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
#finishDrag(state, revert) {
|
|
3615
|
+
const { item, oldIndex, active, onMove, onUp, onKeyDown } = state;
|
|
3616
|
+
|
|
3617
|
+
window.removeEventListener("pointermove", onMove);
|
|
3618
|
+
window.removeEventListener("pointerup", onUp);
|
|
3619
|
+
window.removeEventListener("pointercancel", onUp);
|
|
3620
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
3621
|
+
|
|
3622
|
+
if (active) {
|
|
3623
|
+
if (revert) {
|
|
3624
|
+
this.#restoreItemIndex(item, oldIndex);
|
|
3625
|
+
} else {
|
|
3626
|
+
const newIndex = this.#getElementChildren().indexOf(item);
|
|
3627
|
+
if (newIndex !== -1 && newIndex !== oldIndex) {
|
|
3628
|
+
this.dispatchEvent(
|
|
3629
|
+
new CustomEvent("reorder", {
|
|
3630
|
+
bubbles: true,
|
|
3631
|
+
detail: { oldIndex, newIndex, item },
|
|
3632
|
+
}),
|
|
3633
|
+
);
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
item.classList.remove("dragging");
|
|
3639
|
+
FigReorder.#setDocumentDragging(false);
|
|
3640
|
+
this.#removeIndicator();
|
|
3641
|
+
if (this.#drag === state) this.#drag = null;
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
#restoreItemIndex(item, index) {
|
|
3645
|
+
const items = this.#getElementChildren().filter((node) => node !== item);
|
|
3646
|
+
const ref = items[index] ?? null;
|
|
3647
|
+
if (ref) {
|
|
3648
|
+
this.insertBefore(item, ref);
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
this.appendChild(item);
|
|
3652
|
+
}
|
|
3653
|
+
|
|
3654
|
+
#unbindAll() {
|
|
3655
|
+
for (const [, binding] of this.#bindings) {
|
|
3656
|
+
binding.target.removeEventListener(
|
|
3657
|
+
"pointerdown",
|
|
3658
|
+
binding.onPointerDown,
|
|
3659
|
+
true,
|
|
3660
|
+
);
|
|
3661
|
+
}
|
|
3662
|
+
this.#bindings.clear();
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3665
|
+
#cancelDrag() {
|
|
3666
|
+
if (!this.#drag) return;
|
|
3667
|
+
this.#finishDrag(this.#drag, true);
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
customElements.define("fig-reorder", FigReorder);
|
|
3672
|
+
|