@rogieking/figui3 11.1.5 → 11.2.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/fig-lab.js CHANGED
@@ -2902,675 +2902,3 @@ class FigCanvasControl extends HTMLElement {
2902
2902
  }
2903
2903
  }
2904
2904
  figLabDefineElement("fig-canvas-control", FigCanvasControl);
2905
- /* Reorder wrapper */
2906
- /**
2907
- * @attr {string} axis - Reorder direction: "vertical" (default) or "horizontal"
2908
- * @attr {string} handle - Selector for the drag handle within each item
2909
- * @attr {string} items - Selector limiting which direct children are reorderable
2910
- * @attr {string} indicator - Drop indicator style: "default" or "ring"
2911
- * @attr {boolean} disabled - Disable reordering
2912
- */
2913
- class FigReorder extends HTMLElement {
2914
- static observedAttributes = [
2915
- "axis",
2916
- "handle",
2917
- "items",
2918
- "indicator",
2919
- "disabled",
2920
- ];
2921
-
2922
- static #DRAG_THRESHOLD = 6;
2923
-
2924
- static #NESTED_DRAG_SELECTORS = [
2925
- '[draggable]:not([draggable="false"])',
2926
- 'img:not([draggable="false"])',
2927
- 'a[href]:not([draggable="false"])',
2928
- 'input[type="range"]',
2929
- "fig-handle",
2930
- "fig-slider",
2931
- "fig-input-number",
2932
- "fig-input-gradient",
2933
- "fig-easing-curve",
2934
- "fig-angle",
2935
- "fig-input-audio",
2936
- "fig-input-wheel",
2937
- "fig-joystick",
2938
- "fig-origin-grid",
2939
- "fig-chooser",
2940
- "fig-canvas-control",
2941
- ];
2942
-
2943
- static #OWNED_ATTRIBUTES = new Set([
2944
- "data-reorder-item",
2945
- "data-reorder-generated-role",
2946
- "data-reorder-handle",
2947
- "role",
2948
- "tabindex",
2949
- "aria-label",
2950
- "aria-roledescription",
2951
- ]);
2952
-
2953
- #childObserver = null;
2954
- #attributeObserver = null;
2955
- #attributeSyncQueued = false;
2956
- #bindings = new Map();
2957
- #drag = null;
2958
- #indicator = null;
2959
- #liveRegion = null;
2960
-
2961
- connectedCallback() {
2962
- this.style.display = "contents";
2963
- if (!this.hasAttribute("role")) this.setAttribute("role", "list");
2964
- if (!this.hasAttribute("aria-label")) {
2965
- this.setAttribute("aria-label", "Reorderable list");
2966
- }
2967
- this.#ensureLiveRegion();
2968
- this.#syncChildren();
2969
- this.#childObserver = new MutationObserver(() => this.#syncChildren());
2970
- this.#childObserver.observe(this, { childList: true });
2971
- this.#syncAttributeObserver();
2972
- }
2973
-
2974
- disconnectedCallback() {
2975
- this.#childObserver?.disconnect();
2976
- this.#childObserver = null;
2977
- this.#attributeObserver?.disconnect();
2978
- this.#attributeObserver = null;
2979
- this.#unbindAll();
2980
- this.#cancelDrag();
2981
- this.#removeIndicator();
2982
- this.#liveRegion?.remove();
2983
- this.#liveRegion = null;
2984
- }
2985
-
2986
- attributeChangedCallback(name) {
2987
- if (this.isConnected) {
2988
- if (this.#disabled) this.#cancelDrag();
2989
- if (name === "indicator") this.#syncIndicatorStyle();
2990
- this.#syncAttributeObserver();
2991
- this.#syncChildren();
2992
- }
2993
- }
2994
-
2995
- refresh() {
2996
- if (this.isConnected) this.#syncChildren();
2997
- }
2998
-
2999
- #syncAttributeObserver() {
3000
- if (!this.#itemsSelector) {
3001
- this.#attributeObserver?.disconnect();
3002
- this.#attributeObserver = null;
3003
- return;
3004
- }
3005
- if (this.#attributeObserver) return;
3006
- this.#attributeObserver = new MutationObserver((records) => {
3007
- const relevant = records.some(
3008
- (record) =>
3009
- record.target.parentElement === this &&
3010
- !FigReorder.#OWNED_ATTRIBUTES.has(record.attributeName) &&
3011
- !(record.attributeName === "class" && record.target === this.#drag?.item),
3012
- );
3013
- if (!relevant || this.#attributeSyncQueued) return;
3014
- this.#attributeSyncQueued = true;
3015
- queueMicrotask(() => {
3016
- this.#attributeSyncQueued = false;
3017
- if (this.isConnected) this.#syncChildren();
3018
- });
3019
- });
3020
- this.#attributeObserver.observe(this, { subtree: true, attributes: true });
3021
- }
3022
-
3023
- get #disabled() {
3024
- return (
3025
- this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false"
3026
- );
3027
- }
3028
-
3029
- get #axis() {
3030
- const value = (this.getAttribute("axis") || "vertical").trim().toLowerCase();
3031
- return value === "horizontal" ? "horizontal" : "vertical";
3032
- }
3033
-
3034
- get #handleSelector() {
3035
- return (this.getAttribute("handle") || "").trim();
3036
- }
3037
-
3038
- get #itemsSelector() {
3039
- return (this.getAttribute("items") || "").trim();
3040
- }
3041
-
3042
- get #indicatorStyle() {
3043
- return (this.getAttribute("indicator") || "default").trim().toLowerCase();
3044
- }
3045
-
3046
- #getAllElementChildren() {
3047
- return [...this.children].filter(
3048
- (node) =>
3049
- node.nodeType === Node.ELEMENT_NODE &&
3050
- !node.hasAttribute("data-reorder-live"),
3051
- );
3052
- }
3053
-
3054
- #getElementChildren() {
3055
- const children = this.#getAllElementChildren();
3056
- const selector = this.#itemsSelector;
3057
- if (!selector) return children;
3058
- try {
3059
- return children.filter((child) => child.matches(selector));
3060
- } catch {
3061
- return [];
3062
- }
3063
- }
3064
-
3065
- #syncChildren() {
3066
- const allChildren = this.#getAllElementChildren();
3067
- const children = this.#getElementChildren();
3068
- const childSet = new Set(children);
3069
-
3070
- for (const [child, binding] of this.#bindings) {
3071
- if (!childSet.has(child)) {
3072
- binding.target.removeEventListener(
3073
- "pointerdown",
3074
- binding.onPointerDown,
3075
- true,
3076
- );
3077
- binding.target.removeEventListener("keydown", binding.onKeyDown);
3078
- this.#restoreKeyboardAttrs(binding);
3079
- this.#bindings.delete(child);
3080
- }
3081
- }
3082
-
3083
- this.#clearHandleMarks(allChildren);
3084
- this.#clearReorderItemMarks(allChildren);
3085
- this.#markNonItems(allChildren.filter((child) => !childSet.has(child)));
3086
- const reorderable = !this.#disabled && children.length >= 2;
3087
- this.#markReorderItems(children, reorderable);
3088
-
3089
- if (!reorderable) {
3090
- this.#unbindAll();
3091
- return;
3092
- }
3093
-
3094
- for (const child of children) {
3095
- const target = this.#getDragTarget(child);
3096
- const existing = this.#bindings.get(child);
3097
- if (existing && existing.target === target) continue;
3098
- if (existing) {
3099
- existing.target.removeEventListener(
3100
- "pointerdown",
3101
- existing.onPointerDown,
3102
- true,
3103
- );
3104
- this.#bindings.delete(child);
3105
- }
3106
- if (!target) continue;
3107
- this.#bindChild(child, target);
3108
- }
3109
-
3110
- this.#markHandles(children);
3111
- }
3112
-
3113
- #clearReorderItemMarks(children) {
3114
- for (const child of children) {
3115
- child.removeAttribute("data-reorder-item");
3116
- if (child.hasAttribute("data-reorder-generated-role")) {
3117
- child.removeAttribute("role");
3118
- child.removeAttribute("data-reorder-generated-role");
3119
- }
3120
- }
3121
- }
3122
-
3123
- #markReorderItems(children, reorderable = true) {
3124
- for (const child of children) {
3125
- if (reorderable) child.setAttribute("data-reorder-item", "");
3126
- if (!child.hasAttribute("role")) {
3127
- child.setAttribute("role", "listitem");
3128
- child.setAttribute("data-reorder-generated-role", "");
3129
- }
3130
- }
3131
- }
3132
-
3133
- #markNonItems(children) {
3134
- for (const child of children) {
3135
- if (!child.hasAttribute("role")) {
3136
- child.setAttribute("role", "none");
3137
- child.setAttribute("data-reorder-generated-role", "");
3138
- }
3139
- }
3140
- }
3141
-
3142
- #clearHandleMarks(children) {
3143
- for (const child of children) {
3144
- child
3145
- .querySelectorAll("[data-reorder-handle]")
3146
- .forEach((node) => {
3147
- node.removeAttribute("data-reorder-handle");
3148
- node.removeAttribute("aria-roledescription");
3149
- });
3150
- }
3151
- }
3152
-
3153
- #markHandles(children) {
3154
- if (!this.#handleSelector) return;
3155
- for (const child of children) {
3156
- const handle = child.querySelector(this.#handleSelector);
3157
- if (handle) {
3158
- handle.setAttribute("data-reorder-handle", "");
3159
- handle.setAttribute("aria-roledescription", "reorder handle");
3160
- }
3161
- }
3162
- }
3163
-
3164
- #getDragTarget(child) {
3165
- if (this.#handleSelector) {
3166
- return child.querySelector(this.#handleSelector);
3167
- }
3168
- return child;
3169
- }
3170
-
3171
- #bindChild(child, target) {
3172
- const onPointerDown = (event) => {
3173
- if (this.#disabled || this.#getElementChildren().length < 2) return;
3174
- if (event.button !== 0) return;
3175
- if (
3176
- !this.#handleSelector &&
3177
- this.#isNestedDragTarget(event.target, child)
3178
- ) {
3179
- return;
3180
- }
3181
- this.#startPendingDrag(event, child, target);
3182
- };
3183
- const originalTabIndex = target.getAttribute("tabindex");
3184
- const originalAriaLabel = target.getAttribute("aria-label");
3185
- const originalRole = target.getAttribute("role");
3186
- const itemName =
3187
- child.getAttribute("aria-label") ||
3188
- child.textContent?.trim().replace(/\s+/g, " ").slice(0, 80) ||
3189
- "item";
3190
- target.setAttribute("tabindex", "0");
3191
- target.setAttribute("aria-label", `Move ${itemName}`);
3192
- if (target !== child && !target.hasAttribute("role")) {
3193
- target.setAttribute("role", "button");
3194
- }
3195
- const onKeyDown = (event) => {
3196
- if (this.#disabled) return;
3197
- const horizontal = this.#axis === "horizontal";
3198
- const previousKey = horizontal ? "ArrowLeft" : "ArrowUp";
3199
- const nextKey = horizontal ? "ArrowRight" : "ArrowDown";
3200
- if (
3201
- event.key !== previousKey &&
3202
- event.key !== nextKey &&
3203
- event.key !== "Home" &&
3204
- event.key !== "End"
3205
- ) {
3206
- return;
3207
- }
3208
- event.preventDefault();
3209
- event.stopPropagation();
3210
- const items = this.#getElementChildren();
3211
- const oldIndex = items.indexOf(child);
3212
- let newIndex = oldIndex;
3213
- if (event.key === previousKey) newIndex = Math.max(0, oldIndex - 1);
3214
- else if (event.key === nextKey) {
3215
- newIndex = Math.min(items.length - 1, oldIndex + 1);
3216
- } else if (event.key === "Home") newIndex = 0;
3217
- else if (event.key === "End") newIndex = items.length - 1;
3218
- if (newIndex === oldIndex) {
3219
- this.#announce(`${itemName}, position ${oldIndex + 1} of ${items.length}`);
3220
- return;
3221
- }
3222
- this.#moveItemToFinalIndex(child, newIndex);
3223
- this.#syncChildren();
3224
- this.#getDragTarget(child)?.focus();
3225
- this.dispatchEvent(
3226
- new CustomEvent("reorder", {
3227
- bubbles: true,
3228
- detail: { oldIndex, newIndex, item: child },
3229
- }),
3230
- );
3231
- this.#announce(`${itemName}, position ${newIndex + 1} of ${items.length}`);
3232
- };
3233
-
3234
- target.addEventListener("pointerdown", onPointerDown, true);
3235
- target.addEventListener("keydown", onKeyDown);
3236
- this.#bindings.set(child, {
3237
- target,
3238
- onPointerDown,
3239
- onKeyDown,
3240
- originalTabIndex,
3241
- originalAriaLabel,
3242
- originalRole,
3243
- });
3244
- }
3245
-
3246
- #restoreKeyboardAttrs(binding) {
3247
- const { target, originalTabIndex, originalAriaLabel, originalRole } = binding;
3248
- if (originalTabIndex === null) target.removeAttribute("tabindex");
3249
- else target.setAttribute("tabindex", originalTabIndex);
3250
- if (originalAriaLabel === null) target.removeAttribute("aria-label");
3251
- else target.setAttribute("aria-label", originalAriaLabel);
3252
- if (originalRole === null) target.removeAttribute("role");
3253
- else target.setAttribute("role", originalRole);
3254
- }
3255
-
3256
- #ensureLiveRegion() {
3257
- if (this.#liveRegion?.isConnected) return;
3258
- const region = document.createElement("span");
3259
- region.setAttribute("data-reorder-live", "");
3260
- region.setAttribute("role", "status");
3261
- region.setAttribute("aria-live", "polite");
3262
- region.setAttribute("aria-atomic", "true");
3263
- Object.assign(region.style, {
3264
- position: "absolute",
3265
- width: "1px",
3266
- height: "1px",
3267
- padding: "0",
3268
- margin: "-1px",
3269
- overflow: "hidden",
3270
- clip: "rect(0, 0, 0, 0)",
3271
- whiteSpace: "nowrap",
3272
- border: "0",
3273
- });
3274
- this.#liveRegion = region;
3275
- document.body.appendChild(region);
3276
- }
3277
-
3278
- #announce(message) {
3279
- if (!this.#liveRegion) return;
3280
- this.#liveRegion.textContent = "";
3281
- requestAnimationFrame(() => {
3282
- if (this.#liveRegion) this.#liveRegion.textContent = message;
3283
- });
3284
- }
3285
-
3286
- #isNestedDragTarget(target, child) {
3287
- let node = target;
3288
- while (node && node !== child) {
3289
- if (node instanceof Element) {
3290
- for (const selector of FigReorder.#NESTED_DRAG_SELECTORS) {
3291
- if (node.matches(selector)) return true;
3292
- }
3293
- }
3294
- node = node.parentElement;
3295
- }
3296
- return false;
3297
- }
3298
-
3299
- static #DRAGGING_BODY_CLASS = "fig-reorder-dragging";
3300
-
3301
- static #setDocumentDragging(active) {
3302
- document.body.classList.toggle(FigReorder.#DRAGGING_BODY_CLASS, active);
3303
- }
3304
-
3305
- #startPendingDrag(event, item, target) {
3306
- this.#cancelDrag();
3307
-
3308
- const state = {
3309
- item,
3310
- target,
3311
- pointerId: event.pointerId,
3312
- oldIndex: this.#getElementChildren().indexOf(item),
3313
- targetIndex: this.#getElementChildren().indexOf(item),
3314
- startX: event.clientX,
3315
- startY: event.clientY,
3316
- active: false,
3317
- onMove: null,
3318
- onUp: null,
3319
- onKeyDown: null,
3320
- };
3321
-
3322
- state.onMove = (moveEvent) => {
3323
- if (moveEvent.pointerId !== state.pointerId) return;
3324
-
3325
- const dx = moveEvent.clientX - state.startX;
3326
- const dy = moveEvent.clientY - state.startY;
3327
-
3328
- if (!state.active) {
3329
- if (dx * dx + dy * dy < FigReorder.#DRAG_THRESHOLD * FigReorder.#DRAG_THRESHOLD) {
3330
- return;
3331
- }
3332
- state.active = true;
3333
- event.preventDefault();
3334
- event.stopPropagation();
3335
- item.classList.add("dragging");
3336
- FigReorder.#setDocumentDragging(true);
3337
- try {
3338
- target.setPointerCapture(state.pointerId);
3339
- } catch {}
3340
- }
3341
-
3342
- moveEvent.preventDefault();
3343
- const pointer =
3344
- this.#axis === "horizontal" ? moveEvent.clientX : moveEvent.clientY;
3345
- const index = this.#getInsertIndex(pointer);
3346
- state.targetIndex = index;
3347
- this.#updateIndicator(index, item);
3348
- };
3349
-
3350
- state.onKeyDown = (keyEvent) => {
3351
- if (keyEvent.key !== "Escape" || !state.active) return;
3352
- keyEvent.preventDefault();
3353
- this.#finishDrag(state, true);
3354
- };
3355
-
3356
- state.onUp = (upEvent) => {
3357
- if (upEvent.pointerId !== state.pointerId) return;
3358
- this.#finishDrag(state, upEvent.type === "pointercancel");
3359
- };
3360
-
3361
- this.#drag = state;
3362
- window.addEventListener("pointermove", state.onMove);
3363
- window.addEventListener("pointerup", state.onUp);
3364
- window.addEventListener("pointercancel", state.onUp);
3365
- window.addEventListener("keydown", state.onKeyDown);
3366
- }
3367
-
3368
- #getInsertIndex(pointer) {
3369
- const items = this.#getElementChildren();
3370
- const horizontal = this.#axis === "horizontal";
3371
-
3372
- for (let i = 0; i < items.length; i++) {
3373
- const rect = items[i].getBoundingClientRect();
3374
- const midpoint = horizontal
3375
- ? rect.left + rect.width / 2
3376
- : rect.top + rect.height / 2;
3377
- if (pointer < midpoint) return i;
3378
- }
3379
-
3380
- return items.length;
3381
- }
3382
-
3383
- #shouldShowIndicator(item, index) {
3384
- const items = this.#getElementChildren();
3385
- const currentIndex = items.indexOf(item);
3386
- if (currentIndex === -1) return false;
3387
-
3388
- const clamped = Math.max(0, Math.min(index, items.length));
3389
-
3390
- if (index === currentIndex) return false;
3391
-
3392
- // Hide only the redundant bottom line when the dragged item is already last.
3393
- if (clamped >= items.length && currentIndex === items.length - 1) {
3394
- return false;
3395
- }
3396
-
3397
- return true;
3398
- }
3399
-
3400
- #getReorderBounds(items) {
3401
- if (!items.length) return null;
3402
-
3403
- let left = Infinity;
3404
- let right = -Infinity;
3405
- let top = Infinity;
3406
- let bottom = -Infinity;
3407
-
3408
- for (const item of items) {
3409
- const rect = item.getBoundingClientRect();
3410
- left = Math.min(left, rect.left);
3411
- right = Math.max(right, rect.right);
3412
- top = Math.min(top, rect.top);
3413
- bottom = Math.max(bottom, rect.bottom);
3414
- }
3415
-
3416
- return {
3417
- left,
3418
- top,
3419
- width: right - left,
3420
- height: bottom - top,
3421
- };
3422
- }
3423
-
3424
- #ensureIndicator() {
3425
- if (this.#indicator) return this.#indicator;
3426
-
3427
- const indicator = document.createElement("div");
3428
- indicator.className = "fig-reorder-indicator";
3429
- indicator.setAttribute("data-axis", this.#axis);
3430
- indicator.classList.toggle(
3431
- "fig-reorder-indicator-ring",
3432
- this.#indicatorStyle === "ring",
3433
- );
3434
- document.body.appendChild(indicator);
3435
- this.#indicator = indicator;
3436
- return indicator;
3437
- }
3438
-
3439
- #syncIndicatorStyle() {
3440
- this.#indicator?.classList.toggle(
3441
- "fig-reorder-indicator-ring",
3442
- this.#indicatorStyle === "ring",
3443
- );
3444
- }
3445
-
3446
- #updateIndicator(index, item) {
3447
- const items = this.#getElementChildren();
3448
- if (!this.#shouldShowIndicator(item, index)) {
3449
- this.#removeIndicator();
3450
- return;
3451
- }
3452
-
3453
- const bounds = this.#getReorderBounds(items);
3454
- if (!bounds) {
3455
- this.#removeIndicator();
3456
- return;
3457
- }
3458
-
3459
- const indicator = this.#ensureIndicator();
3460
- indicator.setAttribute("data-axis", this.#axis);
3461
-
3462
- if (this.#axis === "horizontal") {
3463
- let x;
3464
- if (index <= 0) {
3465
- x = items[0].getBoundingClientRect().left;
3466
- } else if (index >= items.length) {
3467
- x = items[items.length - 1].getBoundingClientRect().right;
3468
- } else {
3469
- x = items[index].getBoundingClientRect().left;
3470
- }
3471
-
3472
- indicator.style.left = `${x - 1}px`;
3473
- indicator.style.top = `${bounds.top}px`;
3474
- indicator.style.width = "2px";
3475
- indicator.style.height = `${bounds.height}px`;
3476
- return;
3477
- }
3478
-
3479
- let y;
3480
- if (index <= 0) {
3481
- y = items[0].getBoundingClientRect().top;
3482
- } else if (index >= items.length) {
3483
- y = items[items.length - 1].getBoundingClientRect().bottom;
3484
- } else {
3485
- y = items[index].getBoundingClientRect().top;
3486
- }
3487
-
3488
- indicator.style.left = `${bounds.left}px`;
3489
- indicator.style.top = `${y - 1}px`;
3490
- indicator.style.width = `${bounds.width}px`;
3491
- indicator.style.height = "2px";
3492
- }
3493
-
3494
- #removeIndicator() {
3495
- this.#indicator?.remove();
3496
- this.#indicator = null;
3497
- }
3498
-
3499
- #moveItemToIndex(item, index) {
3500
- const items = this.#getElementChildren();
3501
- const clamped = Math.max(0, Math.min(index, items.length));
3502
-
3503
- if (clamped >= items.length) {
3504
- const last = items[items.length - 1];
3505
- if (last && last !== item) last.after(item);
3506
- return;
3507
- }
3508
-
3509
- const ref = items[clamped];
3510
- if (ref !== item) this.insertBefore(item, ref);
3511
- }
3512
-
3513
- #moveItemToFinalIndex(item, newIndex) {
3514
- const items = this.#getElementChildren().filter((candidate) => candidate !== item);
3515
- const ref = items[newIndex] ?? null;
3516
- if (ref) this.insertBefore(item, ref);
3517
- else if (items.length) items[items.length - 1].after(item);
3518
- else this.appendChild(item);
3519
- }
3520
-
3521
- #finishDrag(state, revert) {
3522
- const { item, oldIndex, active, onMove, onUp, onKeyDown } = state;
3523
-
3524
- window.removeEventListener("pointermove", onMove);
3525
- window.removeEventListener("pointerup", onUp);
3526
- window.removeEventListener("pointercancel", onUp);
3527
- window.removeEventListener("keydown", onKeyDown);
3528
-
3529
- if (active) {
3530
- if (!revert) {
3531
- this.#moveItemToIndex(item, state.targetIndex);
3532
- const newIndex = this.#getElementChildren().indexOf(item);
3533
- if (newIndex !== -1 && newIndex !== oldIndex) {
3534
- this.dispatchEvent(
3535
- new CustomEvent("reorder", {
3536
- bubbles: true,
3537
- detail: { oldIndex, newIndex, item },
3538
- }),
3539
- );
3540
- const name =
3541
- item.getAttribute("aria-label") ||
3542
- item.textContent?.trim().replace(/\s+/g, " ").slice(0, 80) ||
3543
- "item";
3544
- this.#announce(
3545
- `${name}, position ${newIndex + 1} of ${this.#getElementChildren().length}`,
3546
- );
3547
- }
3548
- }
3549
- }
3550
-
3551
- item.classList.remove("dragging");
3552
- FigReorder.#setDocumentDragging(false);
3553
- this.#removeIndicator();
3554
- if (this.#drag === state) this.#drag = null;
3555
- }
3556
-
3557
- #unbindAll() {
3558
- for (const [, binding] of this.#bindings) {
3559
- binding.target.removeEventListener(
3560
- "pointerdown",
3561
- binding.onPointerDown,
3562
- true,
3563
- );
3564
- binding.target.removeEventListener("keydown", binding.onKeyDown);
3565
- this.#restoreKeyboardAttrs(binding);
3566
- }
3567
- this.#bindings.clear();
3568
- }
3569
-
3570
- #cancelDrag() {
3571
- if (!this.#drag) return;
3572
- this.#finishDrag(this.#drag, true);
3573
- }
3574
- }
3575
-
3576
- figLabDefineElement("fig-reorder", FigReorder);