@hyperframes/studio 0.7.58 → 0.7.59
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/dist/assets/{hyperframes-player-CtTDO63S.js → hyperframes-player-Bm07FLkl.js} +1 -1
- package/dist/assets/{index-C47jAC3Q.js → index-B995FG46.js} +1 -1
- package/dist/assets/index-CrkAdJkb.js +426 -0
- package/dist/assets/index-Dj5p8U_A.css +1 -0
- package/dist/assets/{index-DeQPzqwH.js → index-FvzmPhfG.js} +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.html +2 -2
- package/dist/index.js +13514 -8528
- package/dist/index.js.map +1 -1
- package/dist/styles/tailwind-preset.d.ts +5 -0
- package/dist/styles/tailwind-preset.js +8 -1
- package/dist/styles/tailwind-preset.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +3 -3
- package/src/components/DesignPanelPromoteProvider.tsx +15 -2
- package/src/components/StudioRightPanel.tsx +29 -51
- package/src/components/editor/AnimationCard.test.tsx +134 -0
- package/src/components/editor/AnimationCard.tsx +19 -4
- package/src/components/editor/ArcPathControls.tsx +1 -0
- package/src/components/editor/GestureRecordControl.tsx +7 -1
- package/src/components/editor/GsapAnimationSection.tsx +109 -14
- package/src/components/editor/InspectorHeaderActions.tsx +42 -4
- package/src/components/editor/PropertyPanel.test.tsx +948 -0
- package/src/components/editor/PropertyPanel.tsx +144 -148
- package/src/components/editor/PropertyPanelEmptyState.test.tsx +74 -0
- package/src/components/editor/PropertyPanelEmptyState.tsx +179 -1
- package/src/components/editor/PropertyPanelFlat.tsx +580 -0
- package/src/components/editor/PropertyPanelFlatFooter.test.tsx +83 -0
- package/src/components/editor/PropertyPanelFlatFooter.tsx +73 -0
- package/src/components/editor/PropertyPanelFlatHeader.test.tsx +79 -0
- package/src/components/editor/PropertyPanelFlatHeader.tsx +112 -0
- package/src/components/editor/gsapAnimationCallbacks.ts +27 -0
- package/src/components/editor/gsapLivePreview.ts +34 -0
- package/src/components/editor/manualEditingAvailability.test.ts +10 -0
- package/src/components/editor/manualEditingAvailability.ts +9 -0
- package/src/components/editor/propertyPanel3dTransform.tsx +5 -0
- package/src/components/editor/propertyPanelColor.test.tsx +28 -0
- package/src/components/editor/propertyPanelColor.tsx +31 -1
- package/src/components/editor/propertyPanelColorGradingControls.tsx +11 -2
- package/src/components/editor/propertyPanelColorGradingSection.tsx +45 -365
- package/src/components/editor/propertyPanelColorGradingSlider.tsx +19 -5
- package/src/components/editor/propertyPanelFill.tsx +22 -5
- package/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +622 -0
- package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +570 -0
- package/src/components/editor/propertyPanelFlatLayoutSection.test.tsx +293 -0
- package/src/components/editor/propertyPanelFlatLayoutSection.tsx +383 -0
- package/src/components/editor/propertyPanelFlatMaskInsetRows.tsx +102 -0
- package/src/components/editor/propertyPanelFlatMediaSection.test.tsx +436 -0
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +286 -0
- package/src/components/editor/propertyPanelFlatMotionSection.test.tsx +272 -0
- package/src/components/editor/propertyPanelFlatMotionSection.tsx +315 -0
- package/src/components/editor/propertyPanelFlatPrimitives.test.tsx +1174 -0
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +563 -0
- package/src/components/editor/propertyPanelFlatSelectRow.tsx +101 -0
- package/src/components/editor/propertyPanelFlatStyleHelpers.test.ts +23 -0
- package/src/components/editor/propertyPanelFlatStyleHelpers.ts +27 -0
- package/src/components/editor/propertyPanelFlatStyleSections.test.tsx +598 -0
- package/src/components/editor/propertyPanelFlatStyleSections.tsx +516 -0
- package/src/components/editor/propertyPanelFlatTextSection.test.tsx +465 -0
- package/src/components/editor/propertyPanelFlatTextSection.tsx +407 -0
- package/src/components/editor/propertyPanelFlatTimingDerivation.test.ts +71 -0
- package/src/components/editor/propertyPanelFlatTimingDerivation.ts +59 -0
- package/src/components/editor/propertyPanelFlatToggle.test.tsx +63 -0
- package/src/components/editor/propertyPanelFlatToggle.tsx +53 -0
- package/src/components/editor/propertyPanelFont.test.tsx +30 -0
- package/src/components/editor/propertyPanelFont.tsx +136 -95
- package/src/components/editor/propertyPanelHelpers.ts +73 -0
- package/src/components/editor/propertyPanelInputCoverage.test.tsx +587 -0
- package/src/components/editor/propertyPanelMediaSection.tsx +10 -0
- package/src/components/editor/propertyPanelPrimitives.tsx +60 -29
- package/src/components/editor/propertyPanelSections.test.tsx +161 -0
- package/src/components/editor/propertyPanelSections.tsx +154 -90
- package/src/components/editor/propertyPanelStyleSections.tsx +12 -1
- package/src/components/editor/propertyPanelTimingSection.tsx +1 -1
- package/src/components/editor/propertyPanelTypes.ts +14 -1
- package/src/components/editor/propertyPanelValueTier.test.ts +32 -0
- package/src/components/editor/propertyPanelValueTier.ts +28 -0
- package/src/components/editor/useColorGradingController.test.ts +418 -0
- package/src/components/editor/useColorGradingController.ts +596 -0
- package/src/components/panels/VariablesPanel.tsx +4 -0
- package/src/components/sidebar/CompositionsTab.test.ts +28 -2
- package/src/components/sidebar/CompositionsTab.tsx +3 -1
- package/src/components/storyboard/FramePoster.tsx +24 -4
- package/src/components/storyboard/StoryboardFrameFocus.tsx +4 -0
- package/src/components/storyboard/StoryboardFrameTile.tsx +30 -1
- package/src/components/storyboard/StoryboardGrid.tsx +23 -1
- package/src/components/storyboard/StoryboardLoaded.tsx +58 -2
- package/src/components/storyboard/StoryboardView.tsx +9 -2
- package/src/components/storyboard/frameComments.test.ts +95 -0
- package/src/components/storyboard/frameComments.ts +115 -0
- package/src/components/storyboard/useFrameComments.ts +82 -0
- package/src/contexts/DesignPanelInputContext.test.tsx +81 -0
- package/src/contexts/DesignPanelInputContext.tsx +48 -0
- package/src/contexts/DomEditContext.tsx +4 -0
- package/src/contexts/VariablePromoteContext.test.tsx +67 -0
- package/src/contexts/VariablePromoteContext.tsx +10 -5
- package/src/hooks/domEditCommitRunner.ts +11 -0
- package/src/hooks/gsapScriptCommitTypes.ts +6 -4
- package/src/hooks/timelineTrackVisibility.test.ts +52 -0
- package/src/hooks/timelineTrackVisibility.ts +27 -10
- package/src/hooks/useAppHotkeys.ts +9 -0
- package/src/hooks/useDomEditAttributeCommits.ts +123 -1
- package/src/hooks/useDomEditCommits.ts +29 -175
- package/src/hooks/useDomEditCommitsHelpers.ts +158 -0
- package/src/hooks/useDomEditSession.ts +17 -5
- package/src/hooks/useDomEditTextCommits.ts +14 -9
- package/src/hooks/useDomEditWiring.ts +10 -9
- package/src/hooks/useElementLifecycleOps.ts +8 -3
- package/src/hooks/useFileManager.projectOwnership.test.tsx +98 -0
- package/src/hooks/useFileManager.ts +82 -60
- package/src/hooks/useGsapAnimationOps.ts +7 -6
- package/src/hooks/useGsapKeyframeOps.ts +6 -5
- package/src/hooks/useGsapPropertyDebounce.ts +43 -28
- package/src/hooks/useGsapPropertyDebounceFlush.test.ts +35 -0
- package/src/hooks/useGsapScriptCommits.test.tsx +48 -2
- package/src/hooks/useGsapScriptCommits.ts +150 -50
- package/src/hooks/useGsapSelectionHandlers.test.tsx +116 -0
- package/src/hooks/useGsapSelectionHandlers.ts +94 -31
- package/src/hooks/useInspectorSplitResize.ts +51 -0
- package/src/hooks/usePersistentEditHistory.projectOwnership.test.tsx +60 -0
- package/src/hooks/usePersistentEditHistory.test.ts +49 -1
- package/src/hooks/usePersistentEditHistory.ts +71 -31
- package/src/hooks/useProjectCompositionVariables.ts +10 -10
- package/src/hooks/useProjectSignaturePoll.test.tsx +135 -0
- package/src/hooks/useProjectSignaturePoll.ts +68 -0
- package/src/hooks/useSdkSession.lifecycle.test.tsx +137 -0
- package/src/hooks/useSdkSession.ts +155 -14
- package/src/hooks/useSlideshowPersist.ts +7 -1
- package/src/hooks/useStoryboard.ts +23 -4
- package/src/hooks/useTimelineEditing.test.tsx +4 -1
- package/src/hooks/useTimelineEditing.ts +16 -10
- package/src/hooks/useTimelineEditingTypes.ts +3 -0
- package/src/hooks/useTimelineGroupEditing.ts +16 -2
- package/src/hooks/useVariablesPersist.ts +9 -7
- package/src/icons/SystemIcons.tsx +2 -0
- package/src/styles/studio.css +22 -0
- package/src/styles/tailwind-preset.shared.js +7 -0
- package/src/utils/designInputTracking.test.ts +97 -0
- package/src/utils/designInputTracking.ts +80 -0
- package/src/utils/sdkCutover.gate.test.ts +7 -7
- package/src/utils/sdkCutover.test.ts +490 -57
- package/src/utils/sdkCutover.ts +181 -219
- package/src/utils/sdkEditTransaction.ts +292 -0
- package/src/utils/sdkResolverAttempts.ts +94 -0
- package/src/utils/sdkResolverShadow.test.ts +148 -0
- package/src/utils/sdkResolverShadow.ts +139 -106
- package/src/utils/setSlideshowManifest.test.ts +0 -9
- package/src/utils/setSlideshowManifest.ts +20 -27
- package/src/utils/studioFileHistory.test.ts +41 -0
- package/src/utils/studioFileHistory.ts +30 -27
- package/src/utils/studioFileMutationCoordinator.ts +45 -0
- package/src/utils/studioHelpers.test.ts +45 -0
- package/src/utils/studioHelpers.ts +45 -14
- package/src/utils/studioSaveDiagnostics.ts +17 -0
- package/dist/assets/index-B_UvTX3E.js +0 -423
- package/dist/assets/index-uahwWkgw.css +0 -1
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
DesignPanelInputProvider,
|
|
4
|
+
useTrackDesignInput,
|
|
5
|
+
} from "../../contexts/DesignPanelInputContext";
|
|
2
6
|
import { adjustNumericToken, FIELD, LABEL, parseNumericToken } from "./propertyPanelHelpers";
|
|
3
7
|
|
|
4
|
-
function CommitField({
|
|
8
|
+
export function CommitField({
|
|
5
9
|
value,
|
|
6
10
|
disabled,
|
|
7
11
|
liveCommit,
|
|
@@ -116,7 +120,15 @@ export function MetricField({
|
|
|
116
120
|
tooltip?: string;
|
|
117
121
|
onCommit: (nextValue: string) => void;
|
|
118
122
|
}) {
|
|
123
|
+
const track = useTrackDesignInput();
|
|
119
124
|
const scrubRef = useRef<{ startX: number; startValue: number; pointerId: number } | null>(null);
|
|
125
|
+
const commit = useCallback(
|
|
126
|
+
(nextValue: string) => {
|
|
127
|
+
if (nextValue !== value) track("metric", label);
|
|
128
|
+
onCommit(nextValue);
|
|
129
|
+
},
|
|
130
|
+
[label, onCommit, track, value],
|
|
131
|
+
);
|
|
120
132
|
|
|
121
133
|
const handleScrubPointerDown = useCallback(
|
|
122
134
|
(e: React.PointerEvent<HTMLSpanElement>) => {
|
|
@@ -134,9 +146,9 @@ export function MetricField({
|
|
|
134
146
|
const state = scrubRef.current;
|
|
135
147
|
if (!state) return;
|
|
136
148
|
const delta = e.clientX - state.startX;
|
|
137
|
-
|
|
149
|
+
commit(String(Math.round(state.startValue + delta)));
|
|
138
150
|
},
|
|
139
|
-
[
|
|
151
|
+
[commit],
|
|
140
152
|
);
|
|
141
153
|
|
|
142
154
|
const handleScrubPointerUp = useCallback(() => {
|
|
@@ -158,12 +170,7 @@ export function MetricField({
|
|
|
158
170
|
<div className={FIELD} title={tooltip}>
|
|
159
171
|
<div className="flex min-w-0 items-center gap-3">
|
|
160
172
|
<span {...scrubProps}>{label}</span>
|
|
161
|
-
<CommitField
|
|
162
|
-
value={value}
|
|
163
|
-
disabled={disabled}
|
|
164
|
-
liveCommit={liveCommit}
|
|
165
|
-
onCommit={onCommit}
|
|
166
|
-
/>
|
|
173
|
+
<CommitField value={value} disabled={disabled} liveCommit={liveCommit} onCommit={commit} />
|
|
167
174
|
{suffix && <span className="flex-shrink-0 text-[10px] text-neutral-600">{suffix}</span>}
|
|
168
175
|
</div>
|
|
169
176
|
</div>
|
|
@@ -185,17 +192,23 @@ export function DetailField({
|
|
|
185
192
|
disabled?: boolean;
|
|
186
193
|
onCommit: (nextValue: string) => void;
|
|
187
194
|
}) {
|
|
195
|
+
const track = useTrackDesignInput();
|
|
196
|
+
const commit = (nextValue: string) => {
|
|
197
|
+
if (nextValue !== value) track("text", label);
|
|
198
|
+
onCommit(nextValue);
|
|
199
|
+
};
|
|
188
200
|
return (
|
|
189
201
|
<label className="grid min-w-0 gap-1.5">
|
|
190
202
|
<span className={LABEL}>{label}</span>
|
|
191
203
|
<div className={FIELD}>
|
|
192
|
-
<CommitField value={value} disabled={disabled} onCommit={
|
|
204
|
+
<CommitField value={value} disabled={disabled} onCommit={commit} />
|
|
193
205
|
</div>
|
|
194
206
|
</label>
|
|
195
207
|
);
|
|
196
208
|
}
|
|
197
209
|
|
|
198
210
|
export function SliderControl({
|
|
211
|
+
trackName,
|
|
199
212
|
value,
|
|
200
213
|
min,
|
|
201
214
|
max,
|
|
@@ -205,6 +218,7 @@ export function SliderControl({
|
|
|
205
218
|
disabled,
|
|
206
219
|
onCommit,
|
|
207
220
|
}: {
|
|
221
|
+
trackName: string;
|
|
208
222
|
value: number;
|
|
209
223
|
min: number;
|
|
210
224
|
max: number;
|
|
@@ -214,8 +228,10 @@ export function SliderControl({
|
|
|
214
228
|
disabled?: boolean;
|
|
215
229
|
onCommit: (nextValue: number) => void;
|
|
216
230
|
}) {
|
|
231
|
+
const track = useTrackDesignInput();
|
|
217
232
|
const [draft, setDraft] = useState(value);
|
|
218
233
|
const commitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
234
|
+
const interactionChangedRef = useRef(false);
|
|
219
235
|
const valueRef = useRef(value);
|
|
220
236
|
valueRef.current = value;
|
|
221
237
|
|
|
@@ -231,6 +247,10 @@ export function SliderControl({
|
|
|
231
247
|
|
|
232
248
|
const commitDraft = (nextDraft: number) => {
|
|
233
249
|
if (commitTimerRef.current) clearTimeout(commitTimerRef.current);
|
|
250
|
+
if (interactionChangedRef.current) {
|
|
251
|
+
interactionChangedRef.current = false;
|
|
252
|
+
track("slider", trackName);
|
|
253
|
+
}
|
|
234
254
|
if (nextDraft !== valueRef.current) onCommit(nextDraft);
|
|
235
255
|
};
|
|
236
256
|
const scheduleCommit = (nextDraft: number) => {
|
|
@@ -252,6 +272,7 @@ export function SliderControl({
|
|
|
252
272
|
onChange={(e) => {
|
|
253
273
|
const n = Number(e.target.value);
|
|
254
274
|
setDraft(n);
|
|
275
|
+
interactionChangedRef.current = true;
|
|
255
276
|
scheduleCommit(n);
|
|
256
277
|
}}
|
|
257
278
|
onMouseUp={() => commitDraft(draft)}
|
|
@@ -267,16 +288,19 @@ export function SliderControl({
|
|
|
267
288
|
}
|
|
268
289
|
|
|
269
290
|
export function SegmentedControl({
|
|
291
|
+
trackName,
|
|
270
292
|
options,
|
|
271
293
|
value,
|
|
272
294
|
disabled,
|
|
273
295
|
onChange,
|
|
274
296
|
}: {
|
|
297
|
+
trackName: string;
|
|
275
298
|
options: Array<{ label: string; value: string }>;
|
|
276
299
|
value: string;
|
|
277
300
|
disabled?: boolean;
|
|
278
301
|
onChange: (nextValue: string) => void;
|
|
279
302
|
}) {
|
|
303
|
+
const track = useTrackDesignInput();
|
|
280
304
|
return (
|
|
281
305
|
<div
|
|
282
306
|
className="grid min-w-0 gap-[2px] rounded-md bg-panel-input p-[2px]"
|
|
@@ -287,7 +311,10 @@ export function SegmentedControl({
|
|
|
287
311
|
key={option.value}
|
|
288
312
|
type="button"
|
|
289
313
|
disabled={disabled}
|
|
290
|
-
onClick={() =>
|
|
314
|
+
onClick={() => {
|
|
315
|
+
if (option.value !== value) track("segmented", trackName);
|
|
316
|
+
onChange(option.value);
|
|
317
|
+
}}
|
|
291
318
|
className={`min-w-0 truncate rounded px-2 py-[5px] text-[11px] font-medium transition-colors disabled:cursor-not-allowed ${
|
|
292
319
|
option.value === value
|
|
293
320
|
? "bg-panel-hover text-white"
|
|
@@ -314,6 +341,7 @@ export function SelectField({
|
|
|
314
341
|
options: string[];
|
|
315
342
|
onChange: (nextValue: string) => void;
|
|
316
343
|
}) {
|
|
344
|
+
const track = useTrackDesignInput();
|
|
317
345
|
const renderedOptions = value && !options.includes(value) ? [value, ...options] : options;
|
|
318
346
|
return (
|
|
319
347
|
<label className={`${FIELD} flex items-center gap-3`}>
|
|
@@ -321,7 +349,10 @@ export function SelectField({
|
|
|
321
349
|
<select
|
|
322
350
|
value={value}
|
|
323
351
|
disabled={disabled}
|
|
324
|
-
onChange={(e) =>
|
|
352
|
+
onChange={(e) => {
|
|
353
|
+
track("select", label);
|
|
354
|
+
onChange(e.target.value);
|
|
355
|
+
}}
|
|
325
356
|
className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600"
|
|
326
357
|
>
|
|
327
358
|
{renderedOptions.map((option) => (
|
|
@@ -370,24 +401,24 @@ export function Section({
|
|
|
370
401
|
</svg>
|
|
371
402
|
);
|
|
372
403
|
|
|
404
|
+
const section = slugifyPanelSectionTitle(title);
|
|
373
405
|
return (
|
|
374
|
-
<section
|
|
375
|
-
className="min-w-0 border-t border-panel-border"
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
{
|
|
386
|
-
</
|
|
387
|
-
{
|
|
388
|
-
</
|
|
389
|
-
|
|
390
|
-
</section>
|
|
406
|
+
<DesignPanelInputProvider section={section}>
|
|
407
|
+
<section className="min-w-0 border-t border-panel-border" data-panel-section={section}>
|
|
408
|
+
<div className="flex w-full items-center gap-2 px-4 py-2.5">
|
|
409
|
+
<button
|
|
410
|
+
type="button"
|
|
411
|
+
onClick={() => setCollapsed((v) => !v)}
|
|
412
|
+
className="flex min-w-0 flex-1 items-center justify-between gap-2 text-left"
|
|
413
|
+
>
|
|
414
|
+
<h3 className="text-[12px] font-semibold text-panel-text-1">{title}</h3>
|
|
415
|
+
{collapseIcon}
|
|
416
|
+
</button>
|
|
417
|
+
{accessory && <div className="flex flex-shrink-0 items-center">{accessory}</div>}
|
|
418
|
+
</div>
|
|
419
|
+
{!collapsed && <div className="px-4 pb-3">{children}</div>}
|
|
420
|
+
</section>
|
|
421
|
+
</DesignPanelInputProvider>
|
|
391
422
|
);
|
|
392
423
|
}
|
|
393
424
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import React, { act } from "react";
|
|
4
|
+
import { createRoot } from "react-dom/client";
|
|
5
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { FlatTextSection } from "./propertyPanelFlatTextSection";
|
|
7
|
+
import type { DomEditSelection } from "./domEditingTypes";
|
|
8
|
+
|
|
9
|
+
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
document.body.innerHTML = "";
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function makeElement(overrides: Partial<DomEditSelection> = {}): DomEditSelection {
|
|
16
|
+
return {
|
|
17
|
+
element: document.createElement("div"),
|
|
18
|
+
id: "mono-label",
|
|
19
|
+
selector: ".mono-label",
|
|
20
|
+
label: "Mono Label",
|
|
21
|
+
tagName: "div",
|
|
22
|
+
sourceFile: "index.html",
|
|
23
|
+
compositionPath: "index.html",
|
|
24
|
+
isCompositionHost: false,
|
|
25
|
+
isInsideLockedComposition: false,
|
|
26
|
+
boundingBox: { x: 0, y: -24, width: 257, height: 29 },
|
|
27
|
+
textContent: "PACKETS / FRAME",
|
|
28
|
+
dataAttributes: {},
|
|
29
|
+
inlineStyles: {},
|
|
30
|
+
computedStyles: {},
|
|
31
|
+
textFields: [
|
|
32
|
+
{
|
|
33
|
+
key: "field-0",
|
|
34
|
+
label: "Text",
|
|
35
|
+
value: "PACKETS / FRAME",
|
|
36
|
+
tagName: "div",
|
|
37
|
+
attributes: [],
|
|
38
|
+
inlineStyles: { "letter-spacing": "3.96px" },
|
|
39
|
+
computedStyles: {
|
|
40
|
+
"font-family": "JetBrains Mono",
|
|
41
|
+
"font-size": "22px",
|
|
42
|
+
"font-weight": "400",
|
|
43
|
+
"letter-spacing": "3.96px",
|
|
44
|
+
"line-height": "normal",
|
|
45
|
+
"text-align": "right",
|
|
46
|
+
"text-transform": "none",
|
|
47
|
+
"font-style": "normal",
|
|
48
|
+
color: "rgb(255, 176, 32)",
|
|
49
|
+
},
|
|
50
|
+
source: "self",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
capabilities: {
|
|
54
|
+
canSelect: true,
|
|
55
|
+
canEditStyles: true,
|
|
56
|
+
canCrop: true,
|
|
57
|
+
canMove: true,
|
|
58
|
+
canResize: true,
|
|
59
|
+
canApplyManualOffset: true,
|
|
60
|
+
canApplyManualSize: true,
|
|
61
|
+
canApplyManualRotation: true,
|
|
62
|
+
},
|
|
63
|
+
...overrides,
|
|
64
|
+
} as DomEditSelection;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderSection(overrides: Partial<DomEditSelection> = {}) {
|
|
68
|
+
const host = document.createElement("div");
|
|
69
|
+
document.body.append(host);
|
|
70
|
+
const root = createRoot(host);
|
|
71
|
+
const element = makeElement(overrides);
|
|
72
|
+
act(() => {
|
|
73
|
+
root.render(
|
|
74
|
+
<FlatTextSection
|
|
75
|
+
element={element}
|
|
76
|
+
styles={{}}
|
|
77
|
+
fontAssets={[]}
|
|
78
|
+
onSetText={vi.fn()}
|
|
79
|
+
onSetTextFieldStyle={vi.fn()}
|
|
80
|
+
onAddTextField={vi.fn()}
|
|
81
|
+
onRemoveTextField={vi.fn()}
|
|
82
|
+
/>,
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
return { host, root };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
describe("FlatTextSection", () => {
|
|
89
|
+
it("renders the content block and every row from #10a", () => {
|
|
90
|
+
const { host, root } = renderSection();
|
|
91
|
+
expect(host.textContent).toContain("PACKETS / FRAME");
|
|
92
|
+
expect(host.textContent).toContain("Font");
|
|
93
|
+
expect(host.textContent).toContain("Weight");
|
|
94
|
+
expect(host.textContent).toContain("Letter spacing");
|
|
95
|
+
expect(host.textContent).toContain("Line height");
|
|
96
|
+
expect(host.textContent).toContain("Align");
|
|
97
|
+
act(() => root.unmount());
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("colors letter-spacing mint (explicit, differs from 0px default) with a reset button", () => {
|
|
101
|
+
const { host, root } = renderSection();
|
|
102
|
+
const resetButtons = host.querySelectorAll('[data-flat-row-reset="true"]');
|
|
103
|
+
expect(resetButtons.length).toBeGreaterThan(0);
|
|
104
|
+
act(() => root.unmount());
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("commits a font-weight change through onSetTextFieldStyle", () => {
|
|
108
|
+
const onSetTextFieldStyle = vi.fn();
|
|
109
|
+
const host = document.createElement("div");
|
|
110
|
+
document.body.append(host);
|
|
111
|
+
const root = createRoot(host);
|
|
112
|
+
const element = makeElement();
|
|
113
|
+
act(() => {
|
|
114
|
+
root.render(
|
|
115
|
+
<FlatTextSection
|
|
116
|
+
element={element}
|
|
117
|
+
styles={{}}
|
|
118
|
+
fontAssets={[]}
|
|
119
|
+
onSetText={vi.fn()}
|
|
120
|
+
onSetTextFieldStyle={onSetTextFieldStyle}
|
|
121
|
+
onAddTextField={vi.fn()}
|
|
122
|
+
onRemoveTextField={vi.fn()}
|
|
123
|
+
/>,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
const select = host.querySelector<HTMLSelectElement>("select");
|
|
127
|
+
if (!select) throw new Error("expected a weight <select>");
|
|
128
|
+
act(() => {
|
|
129
|
+
select.value = "700";
|
|
130
|
+
select.dispatchEvent(new Event("change", { bubbles: true }));
|
|
131
|
+
});
|
|
132
|
+
expect(onSetTextFieldStyle).toHaveBeenCalledWith("field-0", "font-weight", "700");
|
|
133
|
+
act(() => root.unmount());
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("renders the flat layer list (not the legacy TextSection) for a multi-field element", () => {
|
|
137
|
+
const { host, root } = renderSection({
|
|
138
|
+
textFields: [
|
|
139
|
+
makeElement().textFields[0],
|
|
140
|
+
{
|
|
141
|
+
key: "field-1",
|
|
142
|
+
label: "Text",
|
|
143
|
+
value: "SECOND FIELD",
|
|
144
|
+
tagName: "div",
|
|
145
|
+
attributes: [],
|
|
146
|
+
inlineStyles: {},
|
|
147
|
+
computedStyles: {},
|
|
148
|
+
source: "self",
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
// Legacy TextSection's own Section wrapper (data-panel-section="text")
|
|
153
|
+
// must never render here — multi-field elements now go through the flat
|
|
154
|
+
// FlatTextLayerList + FlatTextFieldEditor path end to end, not a
|
|
155
|
+
// delegation to the legacy component.
|
|
156
|
+
expect(host.querySelector('[data-panel-section="text"]')).toBeNull();
|
|
157
|
+
// The flat layer list's own content must render.
|
|
158
|
+
expect(host.textContent).toContain("Text layers");
|
|
159
|
+
act(() => root.unmount());
|
|
160
|
+
});
|
|
161
|
+
});
|