nudge-ui 0.2.1 → 0.2.3
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 +164 -46
- package/dist/client.mjs +252 -54
- package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
- package/dist/css/value-semantics/colorSemantics.js +17 -0
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
- package/dist/hosts/astro/clientTransport.js +41 -32
- package/dist/hosts/astro/integration.d.ts +2 -2
- package/dist/hosts/astro/integration.js +9 -4
- package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
- package/dist/hosts/astro/markdownIdentity.js +81 -0
- package/dist/hosts/environment.d.ts +15 -0
- package/dist/hosts/environment.js +16 -0
- package/dist/hosts/next/manifest.js +1 -1
- package/dist/hosts/next/sidecar.js +12 -2
- package/dist/hosts/next/wrapper.d.ts +2 -0
- package/dist/hosts/next/wrapper.js +2 -1
- package/dist/hosts/projectBridge.d.ts +1 -0
- package/dist/hosts/projectBridge.js +1 -0
- package/dist/hosts/static/manifest.js +3 -1
- package/dist/hosts/static/server.js +4 -0
- package/dist/hosts/vite/vite.d.ts +4 -0
- package/dist/hosts/vite/vite.js +28 -5
- package/dist/inspector/agent/McpConnectionDialog.d.ts +3 -2
- package/dist/inspector/agent/McpConnectionDialog.js +23 -22
- package/dist/inspector/agent/activity.d.ts +18 -0
- package/dist/inspector/agent/activity.js +70 -0
- package/dist/inspector/agent/client.d.ts +8 -0
- package/dist/inspector/agent/client.js +65 -5
- package/dist/inspector/agent/connectionStatus.d.ts +3 -3
- package/dist/inspector/agent/connectionStatus.js +9 -2
- package/dist/inspector/agent/httpTransport.d.ts +6 -0
- package/dist/inspector/agent/httpTransport.js +50 -3
- package/dist/inspector/agent/protocol.d.ts +9 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +11 -1
- package/dist/inspector/agent/verification.js +53 -25
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
- package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
- package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
- package/dist/inspector/canvas/CanvasCard.js +133 -38
- package/dist/inspector/canvas/CanvasElementOverlay.js +200 -22
- package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
- package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
- package/dist/inspector/canvas/CanvasToolbar.js +8 -4
- package/dist/inspector/canvas/CanvasWorkspace.js +243 -56
- package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
- package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
- package/dist/inspector/canvas/agentPresentation.js +64 -52
- package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
- package/dist/inspector/canvas/canvasGestures.js +1 -1
- package/dist/inspector/canvas/canvasStore.d.ts +46 -82
- package/dist/inspector/canvas/canvasStore.js +251 -284
- package/dist/inspector/canvas/frameContent.d.ts +21 -0
- package/dist/inspector/canvas/frameContent.js +44 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +34 -4
- package/dist/inspector/canvas/frameProtocol.js +9 -2
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -1
- package/dist/inspector/canvas/keyboardShortcuts.js +5 -3
- package/dist/inspector/canvas/layout.d.ts +52 -0
- package/dist/inspector/canvas/layout.js +106 -0
- package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
- package/dist/inspector/canvas/linkEligibility.js +0 -29
- package/dist/inspector/canvas/projection.d.ts +15 -5
- package/dist/inspector/canvas/projection.js +69 -13
- package/dist/inspector/canvas/rendererBootstrap.js +45 -4
- package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
- package/dist/inspector/canvas/rendererElementSelector.js +102 -45
- package/dist/inspector/canvas/rendererStylesheet.js +2 -2
- package/dist/inspector/canvas/resizeHandleScale.js +1 -1
- package/dist/inspector/canvas/sessionStore.d.ts +11 -26
- package/dist/inspector/canvas/sessionStore.js +43 -183
- package/dist/inspector/canvas/staleChangeDetector.js +9 -3
- package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
- package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
- package/dist/inspector/canvas/viewStore.d.ts +38 -0
- package/dist/inspector/canvas/viewStore.js +104 -0
- package/dist/inspector/canvas/wheelPan.d.ts +8 -0
- package/dist/inspector/canvas/wheelPan.js +8 -0
- package/dist/inspector/changes/changesLog.d.ts +6 -5
- package/dist/inspector/changes/changesLog.js +40 -32
- package/dist/inspector/changes/draftChanges.d.ts +56 -0
- package/dist/inspector/changes/draftChanges.js +300 -0
- package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
- package/dist/inspector/changes/previewDiagnostics.js +11 -25
- package/dist/inspector/clientManifest.d.ts +3 -1
- package/dist/inspector/clientManifest.js +1 -0
- package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
- package/dist/inspector/comments/CommentsOverlay.js +250 -0
- package/dist/inspector/comments/element.d.ts +6 -0
- package/dist/inspector/comments/element.js +47 -0
- package/dist/inspector/comments/store.d.ts +29 -0
- package/dist/inspector/comments/store.js +121 -0
- package/dist/inspector/comments/verification.d.ts +5 -0
- package/dist/inspector/comments/verification.js +52 -0
- package/dist/inspector/drafts/model.d.ts +35 -0
- package/dist/inspector/drafts/model.js +45 -0
- package/dist/inspector/drafts/store.d.ts +28 -0
- package/dist/inspector/drafts/store.js +231 -0
- package/dist/inspector/index.js +26 -35
- package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
- package/dist/inspector/iterations/capture.d.ts +2 -0
- package/dist/inspector/iterations/capture.js +173 -0
- package/dist/inspector/iterations/client.d.ts +7 -0
- package/dist/inspector/iterations/client.js +52 -0
- package/dist/inspector/iterations/coordinator.d.ts +15 -0
- package/dist/inspector/iterations/coordinator.js +130 -0
- package/dist/inspector/iterations/prompt.d.ts +3 -0
- package/dist/inspector/iterations/prompt.js +26 -0
- package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
- package/dist/inspector/overlay/DomNavigator.js +46 -0
- package/dist/inspector/overlay/InspectorOverlay.js +6 -2
- package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
- package/dist/inspector/overlay/clickPolicy.js +34 -3
- package/dist/inspector/overlay/elementSelector.d.ts +1 -1
- package/dist/inspector/overlay/elementSelector.js +18 -7
- package/dist/inspector/overlay/interactionStyles.js +3 -1
- package/dist/inspector/overlay/spacingGestures.d.ts +15 -3
- package/dist/inspector/overlay/spacingGestures.js +88 -13
- package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
- package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
- package/dist/inspector/projection/renderedInstance.d.ts +2 -1
- package/dist/inspector/projection/renderedInstance.js +9 -5
- package/dist/inspector/projection/structuralProjection.d.ts +2 -1
- package/dist/inspector/projection/structuralProjection.js +19 -15
- package/dist/inspector/projection/textProjection.d.ts +2 -1
- package/dist/inspector/projection/textProjection.js +8 -4
- package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
- package/dist/inspector/prompt/clipboardHandoff.d.ts +10 -18
- package/dist/inspector/prompt/clipboardHandoff.js +92 -143
- package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
- package/dist/inspector/prompt/generatePrompt.js +17 -16
- package/dist/inspector/runtime/runtimeConfig.d.ts +10 -1
- package/dist/inspector/runtime/runtimeConfig.js +24 -0
- package/dist/inspector/selection/hierarchy.d.ts +6 -0
- package/dist/inspector/selection/hierarchy.js +8 -0
- package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
- package/dist/inspector/settings/SettingsDialog.js +22 -9
- package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
- package/dist/inspector/shell/AgentStatusButton.js +36 -0
- package/dist/inspector/shell/AppShell.js +4 -2
- package/dist/inspector/shell/ChangesLog.js +47 -27
- package/dist/inspector/shell/CopyPromptButton.js +146 -90
- package/dist/inspector/shell/EmptyState.js +9 -8
- package/dist/inspector/shell/InspectorShell.js +14 -25
- package/dist/inspector/shell/PeekOriginalButton.js +24 -23
- package/dist/inspector/shell/originalPreview.js +5 -5
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +11 -0
- package/dist/inspector/sketch/SketchEditor.js +14 -14
- package/dist/inspector/sketch/SketchEntryButton.js +1 -1
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
- package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
- package/dist/inspector/sketch/SketchOverlay.js +7 -7
- package/dist/inspector/sketch/SketchWorkspace.js +18 -12
- package/dist/inspector/sketch/interaction.d.ts +1 -0
- package/dist/inspector/sketch/interaction.js +8 -4
- package/dist/inspector/sketch/model.d.ts +5 -0
- package/dist/inspector/sketch/model.js +6 -2
- package/dist/inspector/sketch/raster.d.ts +1 -2
- package/dist/inspector/sketch/raster.js +68 -4
- package/dist/inspector/sketch/store.js +48 -23
- package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
- package/dist/inspector/styleEditors/BorderEditor.js +49 -78
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
- package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
- package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
- package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
- package/dist/inspector/styleEditors/ColorPicker.js +10 -4
- package/dist/inspector/styleEditors/GapField.js +3 -5
- package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
- package/dist/inspector/styleEditors/GridChildSection.js +18 -9
- package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
- package/dist/inspector/styleEditors/GridPicker.js +8 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/GridValueField.js +19 -14
- package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
- package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
- package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
- package/dist/inspector/styleEditors/LayoutSection.js +32 -11
- package/dist/inspector/styleEditors/SpacingBox.js +16 -9
- package/dist/inspector/styleEditors/Typography.js +11 -3
- package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
- package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
- package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
- package/dist/inspector/styleEditors/gradientValue.js +114 -0
- package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
- package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
- package/dist/inspector/styleEditors/layoutValue.js +0 -42
- package/dist/inspector/styleEditors/nudgeValue.d.ts +4 -4
- package/dist/inspector/styleEditors/nudgeValue.js +41 -11
- package/dist/inspector/tokens/TokenField.d.ts +7 -0
- package/dist/inspector/tokens/TokenField.js +49 -22
- package/dist/inspector/tokens/TokenLabel.js +5 -14
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
- package/dist/inspector/tokens/dragPointerLock.js +70 -0
- package/dist/inspector/tokens/editActions.d.ts +1 -1
- package/dist/inspector/tokens/editActions.js +1 -1
- package/dist/inspector/ui/AtRuleContext.js +4 -6
- package/dist/inspector/ui/Button.d.ts +6 -2
- package/dist/inspector/ui/Button.js +4 -3
- package/dist/inspector/ui/ControlSurface.d.ts +2 -2
- package/dist/inspector/ui/ControlSurface.js +4 -3
- package/dist/inspector/ui/Disclosure.d.ts +22 -0
- package/dist/inspector/ui/Disclosure.js +13 -0
- package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
- package/dist/inspector/ui/InlineStyleWarning.js +13 -0
- package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
- package/dist/inspector/ui/InspectorPopover.js +4 -5
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +8 -7
- package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
- package/dist/inspector/ui/SegmentedControl.js +2 -2
- package/dist/inspector/ui/Select.d.ts +6 -1
- package/dist/inspector/ui/Select.js +7 -11
- package/dist/inspector/ui/SideValuesField.js +1 -1
- package/dist/inspector/ui/Tooltip.d.ts +26 -0
- package/dist/inspector/ui/Tooltip.js +32 -0
- package/dist/inspector/ui/classNames.d.ts +2 -0
- package/dist/inspector/ui/classNames.js +4 -0
- package/dist/inspector/ui/customCursors.d.ts +23 -0
- package/dist/inspector/ui/customCursors.js +122 -0
- package/dist/inspector/ui/dragCursor.d.ts +6 -0
- package/dist/inspector/ui/dragCursor.js +13 -0
- package/dist/inspector/ui/portalContainer.d.ts +2 -0
- package/dist/inspector/ui/portalContainer.js +6 -0
- package/dist/inspector/ui/styles.js +34 -24
- package/dist/inspector/workspace/commands.d.ts +9 -0
- package/dist/inspector/workspace/commands.js +82 -0
- package/dist/inspector/workspace/controller.d.ts +4 -0
- package/dist/inspector/workspace/controller.js +66 -0
- package/dist/inspector/workspace/handoff.d.ts +42 -0
- package/dist/inspector/workspace/handoff.js +115 -0
- package/dist/inspector/workspace/timeline.d.ts +30 -0
- package/dist/inspector/workspace/timeline.js +108 -0
- package/dist/internal/agent-protocol.d.ts +19 -2
- package/dist/internal/agent-protocol.js +13 -0
- package/dist/nudge-ui.mjs +560 -299
- package/dist/project/artifacts.d.ts +13 -0
- package/dist/project/artifacts.js +253 -0
- package/dist/transport/artifacts.d.ts +8 -0
- package/dist/transport/artifacts.js +9 -0
- package/dist/transport/editor.d.ts +20 -1
- package/dist/transport/editor.js +44 -11
- package/dist/transport/routes.d.ts +6 -1
- package/dist/transport/routes.js +6 -1
- package/package.json +6 -2
- package/dist/inspector/changes/workspaceChanges.d.ts +0 -43
- package/dist/inspector/changes/workspaceChanges.js +0 -239
- package/dist/inspector/shell/DomNavigation.d.ts +0 -8
- package/dist/inspector/shell/DomNavigation.js +0 -30
|
@@ -11,6 +11,7 @@ import { projectInspectorValues, projectInspectorValuesForSelection, } from "../
|
|
|
11
11
|
import { getStateStyleValue } from "../shell/stateValue.js";
|
|
12
12
|
import { isZeroCssValue } from "./stylePresence.js";
|
|
13
13
|
import { useFieldVisibility } from "./useFieldVisibility.js";
|
|
14
|
+
import { inlineBlockedBy } from "./inlineAuthored.js";
|
|
14
15
|
export function SpacingBox(props) {
|
|
15
16
|
const { element, selection, entries, tokenRows = [], onAfterEdit } = props;
|
|
16
17
|
const el = element.domElement;
|
|
@@ -35,10 +36,11 @@ export function SpacingField({ property, projection: spacingProjection, domEleme
|
|
|
35
36
|
const sideSlots = SIDE_NAMES.map((side) => {
|
|
36
37
|
const icon = property === "padding" ? _jsx(PaddingSideIndicator, { side: side }) : _jsx(MarginSideIndicator, { side: side });
|
|
37
38
|
const sideName = sideProperty(property, side);
|
|
39
|
+
const field = spacingProjection.fields[side];
|
|
38
40
|
return {
|
|
39
41
|
side,
|
|
40
42
|
icon: null,
|
|
41
|
-
control: (_jsx(TokenField, { property: sideName, selection: selection, tokenRow: findTokenRow(tokenRows, sideName), domElement: el, editTarget: editTarget, entries: entries, suggestions: suggestions, editMetadata: metadataFor(findTokenRow(tokenRows, sideName)), onAfterEdit: onAfterEdit, chipVariant: "small", leading: icon })),
|
|
43
|
+
control: (_jsx(TokenField, { property: sideName, selection: selection, tokenRow: findTokenRow(tokenRows, sideName), domElement: el, editTarget: editTarget, entries: entries, suggestions: suggestions, displayValue: isCalcValue(field.authoredValue) && field.value ? field.value : undefined, editMetadata: metadataFor(findTokenRow(tokenRows, sideName)), onAfterEdit: onAfterEdit, chipVariant: "small", leading: icon })),
|
|
42
44
|
};
|
|
43
45
|
});
|
|
44
46
|
const hasSpacing = SIDE_NAMES.some((side) => {
|
|
@@ -51,9 +53,9 @@ export function SpacingField({ property, projection: spacingProjection, domEleme
|
|
|
51
53
|
const pairSlots = pairDefinitions.map(({ axis, sideProperties }) => ({
|
|
52
54
|
axis,
|
|
53
55
|
icon: null,
|
|
54
|
-
control: (_jsx(PairedTokenField, { displayProperty: `${property}-${axis}`, sideProperties: sideProperties, domElement: el, editTarget: editTarget, axisProjection: spacingProjection.axes[axis], entries: entries, suggestions: suggestions, onAfterEdit: onAfterEdit, chipVariant: "small", leading: _jsx(SpacingAxisIndicator, { property: property, axis: axis }) })),
|
|
56
|
+
control: (_jsx(PairedTokenField, { displayProperty: `${property}-${axis}`, sideProperties: sideProperties, domElement: el, editTarget: editTarget, selection: selection, axisProjection: spacingProjection.axes[axis], entries: entries, suggestions: suggestions, onAfterEdit: onAfterEdit, chipVariant: "small", leading: _jsx(SpacingAxisIndicator, { property: property, axis: axis }) })),
|
|
55
57
|
}));
|
|
56
|
-
return (_jsx(SideValuesField, { label: property, "data-test": property === "inset" ? "layout-inset" : `spacing-${property}`, "data-property": property, resetKey: el, pairedControls: pairSlots, showLabel: showLabel, empty: showEmptyState && !visibility.visible, onAdd: visibility.show, emptyAction: property === "inset" ? (_jsx(IconButton, { variant: "quiet", size: "default", "data-test": "add-inset", label: "Add Inset Values",
|
|
58
|
+
return (_jsx(SideValuesField, { label: property, "data-test": property === "inset" ? "layout-inset" : `spacing-${property}`, "data-property": property, resetKey: el, pairedControls: pairSlots, showLabel: showLabel, empty: showEmptyState && !visibility.visible, onAdd: visibility.show, emptyAction: property === "inset" ? (_jsx(IconButton, { variant: "quiet", size: "default", "data-test": "add-inset", label: "Add Inset Values", onClick: visibility.show, children: _jsx(IconPlus, { size: 16, "aria-hidden": "true" }) })) : undefined, sides: sideSlots }));
|
|
57
59
|
}
|
|
58
60
|
function sideProperty(property, side) {
|
|
59
61
|
return property === "inset" ? side : `${property}-${side}`;
|
|
@@ -82,14 +84,15 @@ function SpacingAxisIndicator({ property, axis, }) {
|
|
|
82
84
|
}
|
|
83
85
|
return (_jsxs("svg", { className: "side-values__icon side-values__axis-icon", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [_jsx("rect", { x: "19", y: "6", width: "12", height: "14", rx: "2", transform: "rotate(90 19 6)", stroke: "currentColor", strokeWidth: "var(--icon-stroke-width)" }), _jsx("line", { x1: "19", y1: "2", x2: "5", y2: "2", stroke: "currentColor", strokeWidth: "var(--icon-stroke-width)", strokeLinecap: "round" }), _jsx("line", { x1: "19", y1: "22", x2: "5", y2: "22", stroke: "currentColor", strokeWidth: "var(--icon-stroke-width)", strokeLinecap: "round" })] }));
|
|
84
86
|
}
|
|
85
|
-
function PairedTokenField({ displayProperty, sideProperties, domElement: el, editTarget, axisProjection, entries, suggestions, onAfterEdit, chipVariant, leading, }) {
|
|
87
|
+
function PairedTokenField({ displayProperty, sideProperties, domElement: el, editTarget, selection, axisProjection, entries, suggestions, onAfterEdit, chipVariant, leading, }) {
|
|
86
88
|
const row = pairTokenRow(displayProperty, axisProjection);
|
|
87
89
|
const groupMixed = axisProjection.fields.some((field) => field.value === "Mixed");
|
|
88
90
|
const valuesMatch = axisProjection.state === "shared" && !groupMixed;
|
|
89
91
|
const expression = Boolean(row && (row.capability === "raw" || row.capability === "composite"
|
|
90
92
|
|| row.modifiers?.some((modifier) => modifier.kind === "alpha")));
|
|
91
93
|
const calcAuthored = row?.authored ?? row?.declaredValue ?? "";
|
|
92
|
-
const
|
|
94
|
+
const isCalcAuthored = isCalcValue(calcAuthored);
|
|
95
|
+
const activeTokenName = !valuesMatch || expression || isCalcAuthored ? null : row.tokenName;
|
|
93
96
|
// Some browsers expose the used pixel size for horizontal auto margins.
|
|
94
97
|
// Keep the authored keyword visible because replacing it with that transient
|
|
95
98
|
// size would misrepresent the declaration and change the edit semantics.
|
|
@@ -98,7 +101,7 @@ function PairedTokenField({ displayProperty, sideProperties, domElement: el, edi
|
|
|
98
101
|
: valuesMatch
|
|
99
102
|
? calcAuthored.trim().toLowerCase() === "auto"
|
|
100
103
|
? calcAuthored.trim()
|
|
101
|
-
: expression ? row.authored || row.declaredValue || row.resolvedValue : row.resolvedValue
|
|
104
|
+
: isCalcAuthored ? row.resolvedValue : expression ? row.authored || row.declaredValue || row.resolvedValue : row.resolvedValue
|
|
102
105
|
: axisProjection.fields.map(fieldDisplayValue).join(", ");
|
|
103
106
|
const resolvedValue = valuesMatch
|
|
104
107
|
? row.resolvedValue
|
|
@@ -106,6 +109,7 @@ function PairedTokenField({ displayProperty, sideProperties, domElement: el, edi
|
|
|
106
109
|
const currentToken = activeTokenName
|
|
107
110
|
? entries.find((entry) => entry.name === activeTokenName) ?? null
|
|
108
111
|
: null;
|
|
112
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [el], ...sideProperties);
|
|
109
113
|
function commitAxisValue(value) {
|
|
110
114
|
const pair = splitAxisValue(value);
|
|
111
115
|
const records = setStyles(editTarget ?? el, sideProperties.map((property, index) => ({
|
|
@@ -115,7 +119,7 @@ function PairedTokenField({ displayProperty, sideProperties, domElement: el, edi
|
|
|
115
119
|
if (records.some(Boolean))
|
|
116
120
|
onAfterEdit?.();
|
|
117
121
|
}
|
|
118
|
-
return (_jsx(TokenValueField, { property: displayProperty, committedValue: committedValue, resolvedValue: resolvedValue, activeTokenName: activeTokenName, mixed: groupMixed, atRules: row.atRuleCandidates ?? row.atRules, entries: entries, suggestions: suggestions, chipVariant: chipVariant, formatRawValue: (value) => formatAxisValue(value, displayProperty), onCommitRaw: commitAxisValue, onSelectToken: (chosen) => {
|
|
122
|
+
return (_jsx(TokenValueField, { property: displayProperty, domElement: el, committedValue: committedValue, resolvedValue: resolvedValue, activeTokenName: activeTokenName, mixed: groupMixed, atRules: row.atRuleCandidates ?? row.atRules, entries: entries, suggestions: suggestions, chipVariant: chipVariant, formatRawValue: (value) => formatAxisValue(value, displayProperty), onCommitRaw: commitAxisValue, onSelectToken: (chosen) => {
|
|
119
123
|
const records = swapTokens(editTarget ?? el, sideProperties.map((property) => ({
|
|
120
124
|
property,
|
|
121
125
|
newToken: chosen,
|
|
@@ -123,10 +127,13 @@ function PairedTokenField({ displayProperty, sideProperties, domElement: el, edi
|
|
|
123
127
|
})));
|
|
124
128
|
if (records.some(Boolean))
|
|
125
129
|
onAfterEdit?.();
|
|
126
|
-
}, onUnlink: commitAxisValue, leading: leading }));
|
|
130
|
+
}, onUnlink: commitAxisValue, leading: leading, blockedBy: blockedBy }));
|
|
127
131
|
}
|
|
128
132
|
function fieldDisplayValue(field) {
|
|
129
|
-
return field.authoredValue.trim() || field.value;
|
|
133
|
+
return isCalcValue(field.authoredValue) ? field.value : field.authoredValue.trim() || field.value;
|
|
134
|
+
}
|
|
135
|
+
function isCalcValue(value) {
|
|
136
|
+
return /\bcalc\s*\(/i.test(value);
|
|
130
137
|
}
|
|
131
138
|
function splitAxisValue(value) {
|
|
132
139
|
let depth = 0;
|
|
@@ -9,6 +9,8 @@ import { getStateStyleValue } from "../shell/stateValue.js";
|
|
|
9
9
|
import { setStyle, setStyles } from "../tokens/editActions.js";
|
|
10
10
|
import { AtRuleIndicator, useFieldAtRules } from "../ui/AtRuleContext.js";
|
|
11
11
|
import { ControlSurface } from "../ui/ControlSurface.js";
|
|
12
|
+
import { InlineStyleWarning } from "../ui/InlineStyleWarning.js";
|
|
13
|
+
import { inlineBlockedBy } from "./inlineAuthored.js";
|
|
12
14
|
export function Typography(props) {
|
|
13
15
|
const { element, selection, entries, tokenRows = [], onAfterEdit } = props;
|
|
14
16
|
const el = element.domElement;
|
|
@@ -56,6 +58,7 @@ function FontStyleField({ element, elements, editTarget, fontStyleRow, fontWeigh
|
|
|
56
58
|
const atRules = fontStyleRow?.atRuleCandidates ?? fontStyleRow?.atRules
|
|
57
59
|
?? fontWeightRow?.atRuleCandidates ?? fontWeightRow?.atRules
|
|
58
60
|
?? (fontStyleAtRules.length > 0 ? fontStyleAtRules : fontWeightAtRules);
|
|
61
|
+
const blockedBy = inlineBlockedBy(elements, "font-style", "font-weight");
|
|
59
62
|
const readValue = () => readFontStyle(element);
|
|
60
63
|
const values = elements.map(readFontStyle);
|
|
61
64
|
const isMixed = values.some((value) => value.weight !== values[0]?.weight || value.style !== values[0]?.style);
|
|
@@ -72,6 +75,8 @@ function FontStyleField({ element, elements, editTarget, fontStyleRow, fontWeigh
|
|
|
72
75
|
? FONT_STYLE_OPTIONS
|
|
73
76
|
: [...FONT_STYLE_OPTIONS, { value: currentKey, label: currentLabel, ...current }];
|
|
74
77
|
function handleChange(value) {
|
|
78
|
+
if (blockedBy)
|
|
79
|
+
return;
|
|
75
80
|
const option = options.find((candidate) => candidate.value === value);
|
|
76
81
|
if (!option || option.value === "mixed" || !("weight" in option) || !("style" in option))
|
|
77
82
|
return;
|
|
@@ -82,7 +87,7 @@ function FontStyleField({ element, elements, editTarget, fontStyleRow, fontWeigh
|
|
|
82
87
|
]);
|
|
83
88
|
onAfterEdit?.();
|
|
84
89
|
}
|
|
85
|
-
return (_jsxs(ControlSurface, { className: "typography__field typography__field--font-style", "data-test": "typography-field", "data-property": "font-style", "aria-label": "Font style",
|
|
90
|
+
return (_jsxs(ControlSurface, { className: "typography__field typography__field--font-style", "data-test": "typography-field", "data-property": "font-style", "aria-label": "Font style", children: [_jsx("span", { className: "typography__field-icon", "aria-hidden": "true", children: _jsx(IconTypography, { size: "var(--icon-size-small)", stroke: 1.45 }) }), _jsx(Select, { appearance: "embedded", value: isMixed ? "mixed" : currentKey, options: options.map(({ value, label }) => ({ value, label })), onValueChange: handleChange, disabled: blockedBy !== null, "data-test": "font-style-field" }), _jsx(AtRuleIndicator, { atRules: atRules }), _jsx(InlineStyleWarning, { blockedBy: blockedBy })] }));
|
|
86
91
|
}
|
|
87
92
|
function readFontStyle(element) {
|
|
88
93
|
return {
|
|
@@ -104,6 +109,7 @@ function formatCustomFontStyle(weight, style) {
|
|
|
104
109
|
}
|
|
105
110
|
function AlignmentField({ property, label, element, elements, editTarget, defaultValue, options, onAfterEdit }) {
|
|
106
111
|
const atRules = useFieldAtRules(property);
|
|
112
|
+
const blockedBy = inlineBlockedBy(elements, property);
|
|
107
113
|
const readValues = () => elements.map((target) => getStateStyleValue(target, property, defaultValue));
|
|
108
114
|
const initialValues = readValues();
|
|
109
115
|
const isMixed = initialValues.some((value) => value !== initialValues[0]);
|
|
@@ -115,16 +121,18 @@ function AlignmentField({ property, label, element, elements, editTarget, defaul
|
|
|
115
121
|
setCurrent(values.some((value) => value !== values[0]) ? null : normalizeAlignment(property, next));
|
|
116
122
|
}, [defaultValue, element, elements, property]);
|
|
117
123
|
function handleChange(value) {
|
|
124
|
+
if (blockedBy)
|
|
125
|
+
return;
|
|
118
126
|
setCurrent(value);
|
|
119
127
|
setStyle(editTarget, property, value);
|
|
120
128
|
onAfterEdit?.();
|
|
121
129
|
}
|
|
122
|
-
return (_jsxs("div", { className: "typography__alignment-field", children: [isMixed || current === null && elements.length > 1 ? (_jsx("span", { className: "typography__mixed", "data-test": `typography-mixed-${property}`, children: "Mixed" })) : null, _jsx(SegmentedControl, { value: current, "aria-label": label, "data-property": property, options: options.map((option) => ({
|
|
130
|
+
return (_jsxs("div", { className: "typography__alignment-field", children: [isMixed || current === null && elements.length > 1 ? (_jsx("span", { className: "typography__mixed", "data-test": `typography-mixed-${property}`, children: "Mixed" })) : null, _jsx(SegmentedControl, { value: current, "aria-label": label, "data-property": property, disabled: blockedBy !== null, options: options.map((option) => ({
|
|
123
131
|
value: option.value,
|
|
124
132
|
label: option.label,
|
|
125
133
|
icon: option.icon,
|
|
126
134
|
testId: `typography-align-${property}-${option.value}`,
|
|
127
|
-
})), onChange: handleChange }), _jsx(AtRuleIndicator, { atRules: atRules })] }));
|
|
135
|
+
})), onChange: handleChange }), _jsx(AtRuleIndicator, { atRules: atRules }), _jsx(InlineStyleWarning, { blockedBy: blockedBy })] }));
|
|
128
136
|
}
|
|
129
137
|
function normalizeAlignment(property, value) {
|
|
130
138
|
if (property !== "text-align")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { GradientValue } from "./gradientValue.js";
|
|
2
|
+
export type BackgroundMode = "solid" | "gradient" | "image";
|
|
3
|
+
export interface BackgroundLayer {
|
|
4
|
+
mode: BackgroundMode;
|
|
5
|
+
baseColor: boolean;
|
|
6
|
+
gradient?: GradientValue;
|
|
7
|
+
value: string;
|
|
8
|
+
blend: string;
|
|
9
|
+
size: string;
|
|
10
|
+
repeat: string;
|
|
11
|
+
position: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const BACKGROUND_BLEND_MODES: {
|
|
14
|
+
value: string;
|
|
15
|
+
label: string;
|
|
16
|
+
}[];
|
|
17
|
+
export declare function backgroundMode(value: string): BackgroundMode;
|
|
18
|
+
export declare function newBackgroundLayer(): BackgroundLayer;
|
|
19
|
+
/** CSS repeats shorter background property lists to match the image count. */
|
|
20
|
+
export declare function readBackgroundLayers(read: (property: string, fallback: string) => string): BackgroundLayer[];
|
|
21
|
+
export declare function backgroundDeclarations(layers: BackgroundLayer[]): {
|
|
22
|
+
property: string;
|
|
23
|
+
value: string;
|
|
24
|
+
}[];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { splitTopLevel } from "../../css/value-semantics/cssSyntax.js";
|
|
2
|
+
import { isEmptyColorValue } from "./ColorPicker.js";
|
|
3
|
+
export const BACKGROUND_BLEND_MODES = ["normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"]
|
|
4
|
+
.map((value) => ({ value, label: value.replace(/-/g, " ").replace(/^./, (letter) => letter.toUpperCase()) }));
|
|
5
|
+
export function backgroundMode(value) {
|
|
6
|
+
if (!value || value === "none")
|
|
7
|
+
return "solid";
|
|
8
|
+
return /(?:repeating-)?(?:linear|radial|conic)-gradient\(/.test(value) ? "gradient" : "image";
|
|
9
|
+
}
|
|
10
|
+
export function newBackgroundLayer() {
|
|
11
|
+
return { mode: "solid", baseColor: true, value: "#ffffff", blend: "normal", size: "auto", repeat: "repeat", position: "0% 0%" };
|
|
12
|
+
}
|
|
13
|
+
/** CSS repeats shorter background property lists to match the image count. */
|
|
14
|
+
export function readBackgroundLayers(read) {
|
|
15
|
+
const image = read("background-image", "none");
|
|
16
|
+
const images = image === "none" ? [] : splitTopLevel(image, ",").map((value) => value.trim());
|
|
17
|
+
const listValue = (property, fallback, index) => {
|
|
18
|
+
const values = splitTopLevel(read(property, fallback), ",");
|
|
19
|
+
return values[index % values.length]?.trim() || fallback;
|
|
20
|
+
};
|
|
21
|
+
const layers = images.map((value, index) => {
|
|
22
|
+
const flat = /^linear-gradient\((.*)\)$/s.exec(value);
|
|
23
|
+
const colors = flat ? splitTopLevel(flat[1], ",").map((color) => color.trim()) : [];
|
|
24
|
+
const solid = colors.length === 2 && colors[0] === colors[1];
|
|
25
|
+
return {
|
|
26
|
+
mode: solid ? "solid" : backgroundMode(value),
|
|
27
|
+
baseColor: false,
|
|
28
|
+
value: solid ? colors[0] : value,
|
|
29
|
+
blend: listValue("background-blend-mode", "normal", index),
|
|
30
|
+
size: listValue("background-size", "auto", index),
|
|
31
|
+
repeat: listValue("background-repeat", "repeat", index),
|
|
32
|
+
position: listValue("background-position", "0% 0%", index),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
const color = read("background-color", "transparent");
|
|
36
|
+
if (!isEmptyColorValue(color))
|
|
37
|
+
layers.push({ ...newBackgroundLayer(), value: color });
|
|
38
|
+
return layers;
|
|
39
|
+
}
|
|
40
|
+
export function backgroundDeclarations(layers) {
|
|
41
|
+
const base = layers.at(-1)?.mode === "solid" && layers.at(-1)?.baseColor ? layers.at(-1) : undefined;
|
|
42
|
+
const images = base ? layers.slice(0, -1) : layers;
|
|
43
|
+
return [
|
|
44
|
+
{ property: "background-color", value: base?.value ?? "transparent" },
|
|
45
|
+
{ property: "background-image", value: images.map((layer) => layer.mode === "solid" ? `linear-gradient(${layer.value}, ${layer.value})` : layer.value).join(", ") || "none" },
|
|
46
|
+
...["blend", "size", "repeat", "position"].map((field) => ({
|
|
47
|
+
property: field === "blend" ? "background-blend-mode" : `background-${field}`,
|
|
48
|
+
value: images.map((layer) => layer[field]).join(", ") || newBackgroundLayer()[field],
|
|
49
|
+
})),
|
|
50
|
+
];
|
|
51
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type GradientType = "linear" | "radial" | "conic";
|
|
2
|
+
export interface GradientStop {
|
|
3
|
+
color: string;
|
|
4
|
+
position: number;
|
|
5
|
+
}
|
|
6
|
+
export interface GradientValue {
|
|
7
|
+
type: GradientType;
|
|
8
|
+
repeating: boolean;
|
|
9
|
+
angle: number;
|
|
10
|
+
/** Preserve the computed shape, origin, and interpolation method. */
|
|
11
|
+
prelude: string;
|
|
12
|
+
stops: GradientStop[];
|
|
13
|
+
}
|
|
14
|
+
export declare function defaultGradient(): GradientValue;
|
|
15
|
+
/** Parse editable computed gradients. Leave unsupported syntax in the CSS field. */
|
|
16
|
+
export declare function parseGradient(value: string): GradientValue | null;
|
|
17
|
+
export declare function serializeGradient(gradient: GradientValue): string;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { splitTopLevel, splitTopLevelWhitespace } from "../../css/value-semantics/cssSyntax.js";
|
|
2
|
+
export function defaultGradient() {
|
|
3
|
+
return { type: "linear", repeating: false, angle: 90, prelude: "", stops: [
|
|
4
|
+
{ color: "#dddddd", position: 0 }, { color: "#a4a4a4", position: 100 },
|
|
5
|
+
] };
|
|
6
|
+
}
|
|
7
|
+
function angleDegrees(value) {
|
|
8
|
+
const match = /^([+-]?(?:\d*\.)?\d+)(deg|grad|rad|turn)$/.exec(value);
|
|
9
|
+
if (!match)
|
|
10
|
+
return null;
|
|
11
|
+
const number = Number(match[1]);
|
|
12
|
+
switch (match[2]) {
|
|
13
|
+
case "grad": return number * 0.9;
|
|
14
|
+
case "rad": return number * 180 / Math.PI;
|
|
15
|
+
case "turn": return number * 360;
|
|
16
|
+
default: return number;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Parse editable computed gradients. Leave unsupported syntax in the CSS field. */
|
|
20
|
+
export function parseGradient(value) {
|
|
21
|
+
if (splitTopLevel(value, ",").length !== 1)
|
|
22
|
+
return null;
|
|
23
|
+
const match = /^(repeating-)?(linear|radial|conic)-gradient\((.*)\)$/s.exec(value.trim());
|
|
24
|
+
if (!match)
|
|
25
|
+
return null;
|
|
26
|
+
const type = match[2];
|
|
27
|
+
const parts = splitTopLevel(match[3], ",").map((part) => part.trim());
|
|
28
|
+
let prelude = "";
|
|
29
|
+
let angle = type === "linear" ? 180 : 0;
|
|
30
|
+
const first = parts[0] ?? "";
|
|
31
|
+
if (/^(?:to\s|[+-]?(?:\d*\.)?\d+(?:deg|grad|rad|turn)\b|circle\b|ellipse\b|closest-|farthest-|at\s|from\s|in\s)/.test(first)
|
|
32
|
+
|| (type === "radial" && /\b(?:px|%)\b/.test(first))) {
|
|
33
|
+
prelude = parts.shift();
|
|
34
|
+
if (type === "linear") {
|
|
35
|
+
const direction = /^(to\s+(?:(?:left|right|top|bottom)(?:\s+|$)){1,2})/.exec(prelude)?.[0]?.trim();
|
|
36
|
+
if (direction) {
|
|
37
|
+
const directions = { top: 0, "top right": 45, "right top": 45, right: 90, "bottom right": 135, "right bottom": 135, bottom: 180, "bottom left": 225, "left bottom": 225, left: 270, "top left": 315, "left top": 315 };
|
|
38
|
+
// Corner directions depend on the element's aspect ratio. Keep them as CSS.
|
|
39
|
+
if (direction.slice(3).includes(" "))
|
|
40
|
+
return null;
|
|
41
|
+
angle = directions[direction.slice(3)] ?? 180;
|
|
42
|
+
prelude = prelude.slice(direction.length).trim();
|
|
43
|
+
}
|
|
44
|
+
else if (!prelude.startsWith("in ")) {
|
|
45
|
+
const rawAngle = splitTopLevelWhitespace(prelude)[0];
|
|
46
|
+
const parsed = angleDegrees(rawAngle);
|
|
47
|
+
if (parsed === null)
|
|
48
|
+
return null;
|
|
49
|
+
angle = parsed;
|
|
50
|
+
prelude = prelude.slice(rawAngle.length).trim();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (type === "conic" && prelude.startsWith("from ")) {
|
|
54
|
+
const rawAngle = splitTopLevelWhitespace(prelude)[1];
|
|
55
|
+
const parsed = angleDegrees(rawAngle);
|
|
56
|
+
if (parsed === null)
|
|
57
|
+
return null;
|
|
58
|
+
angle = parsed;
|
|
59
|
+
prelude = prelude.slice(5 + rawAngle.length).trim();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const stops = [];
|
|
63
|
+
for (const part of parts) {
|
|
64
|
+
const words = splitTopLevelWhitespace(part);
|
|
65
|
+
const positions = [];
|
|
66
|
+
while (words.length > 1) {
|
|
67
|
+
const last = words[words.length - 1];
|
|
68
|
+
const percent = /^([+-]?(?:\d*\.)?\d+)%$/.exec(last);
|
|
69
|
+
const degrees = type === "conic" ? angleDegrees(last) : null;
|
|
70
|
+
if (!percent && degrees === null && last !== "0")
|
|
71
|
+
break;
|
|
72
|
+
positions.unshift(percent ? Number(percent[1]) : degrees !== null ? degrees / 3.6 : 0);
|
|
73
|
+
words.pop();
|
|
74
|
+
}
|
|
75
|
+
const color = words.join(" ");
|
|
76
|
+
// Length stops and color hints cannot be represented by a percentage slider.
|
|
77
|
+
if (!color || words.length !== 1 || /^([+-]?\d|calc\(|var\()/i.test(color) || positions.length > 2)
|
|
78
|
+
return null;
|
|
79
|
+
if (positions.length)
|
|
80
|
+
positions.forEach((position) => stops.push({ color, position }));
|
|
81
|
+
else
|
|
82
|
+
stops.push({ color, position: null });
|
|
83
|
+
}
|
|
84
|
+
if (stops.length < 2)
|
|
85
|
+
return null;
|
|
86
|
+
stops[0].position ??= 0;
|
|
87
|
+
stops[stops.length - 1].position ??= 100;
|
|
88
|
+
let previous = stops[0].position;
|
|
89
|
+
for (const stop of stops) {
|
|
90
|
+
if (stop.position !== null) {
|
|
91
|
+
stop.position = Math.max(previous, stop.position);
|
|
92
|
+
previous = stop.position;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
let start = 0;
|
|
96
|
+
while (start < stops.length - 1) {
|
|
97
|
+
let end = start + 1;
|
|
98
|
+
while (stops[end].position === null)
|
|
99
|
+
end++;
|
|
100
|
+
for (let index = start + 1; index < end; index++) {
|
|
101
|
+
stops[index].position = stops[start].position + (stops[end].position - stops[start].position) * (index - start) / (end - start);
|
|
102
|
+
}
|
|
103
|
+
start = end;
|
|
104
|
+
}
|
|
105
|
+
return { type, repeating: Boolean(match[1]), angle, prelude, stops: stops };
|
|
106
|
+
}
|
|
107
|
+
export function serializeGradient(gradient) {
|
|
108
|
+
const prefix = gradient.type === "linear" ? `${gradient.angle}deg`
|
|
109
|
+
: gradient.type === "conic" ? `from ${gradient.angle}deg` : "";
|
|
110
|
+
const prelude = [prefix, gradient.prelude].filter(Boolean).join(" ");
|
|
111
|
+
const stops = [...gradient.stops].sort((a, b) => a.position - b.position)
|
|
112
|
+
.map((stop) => `${stop.color} ${Number(stop.position.toFixed(3))}%`).join(", ");
|
|
113
|
+
return `${gradient.repeating ? "repeating-" : ""}${gradient.type}-gradient(${prelude ? `${prelude}, ` : ""}${stops})`;
|
|
114
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the inline-authored source when `el` carries the property — or a
|
|
3
|
+
* shorthand that sets it — in its `style` attribute. Managed previews can
|
|
4
|
+
* never beat inline styles in the cascade, so a non-null result means the
|
|
5
|
+
* field must present as blocked instead of accepting edits that silently
|
|
6
|
+
* revert (see `verifyPreview`'s "inline-style" conflict).
|
|
7
|
+
*/
|
|
8
|
+
export declare function inlineAuthoredValue(el: HTMLElement, property: string): string | null;
|
|
9
|
+
/** First inline-authored source that blocks any of `properties` on any of `elements`. */
|
|
10
|
+
export declare function inlineBlockedBy(elements: readonly HTMLElement[], ...properties: readonly string[]): string | null;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { isLayoutPreview } from "./layoutPreviewState.js";
|
|
2
|
+
/**
|
|
3
|
+
* Shorthands that also author the keyed longhand when present in a `style`
|
|
4
|
+
* attribute. Managed-stylesheet previews lose the cascade to any of these,
|
|
5
|
+
* so fields must treat the longhand as inline-blocked either way.
|
|
6
|
+
*/
|
|
7
|
+
const SHORTHAND_SOURCES = {
|
|
8
|
+
"font-family": ["font"],
|
|
9
|
+
"font-style": ["font"],
|
|
10
|
+
"font-weight": ["font"],
|
|
11
|
+
"font-size": ["font"],
|
|
12
|
+
"line-height": ["font"],
|
|
13
|
+
"background-color": ["background"],
|
|
14
|
+
"background-image": ["background"],
|
|
15
|
+
"row-gap": ["gap"],
|
|
16
|
+
"column-gap": ["gap"],
|
|
17
|
+
"flex-grow": ["flex"],
|
|
18
|
+
"flex-shrink": ["flex"],
|
|
19
|
+
"flex-basis": ["flex"],
|
|
20
|
+
"flex-direction": ["flex-flow"],
|
|
21
|
+
"flex-wrap": ["flex-flow"],
|
|
22
|
+
"align-content": ["place-content"],
|
|
23
|
+
"justify-content": ["place-content"],
|
|
24
|
+
"align-items": ["place-items"],
|
|
25
|
+
"justify-items": ["place-items"],
|
|
26
|
+
"align-self": ["place-self"],
|
|
27
|
+
"justify-self": ["place-self"],
|
|
28
|
+
"border-width": ["border"],
|
|
29
|
+
"border-style": ["border"],
|
|
30
|
+
"border-color": ["border"],
|
|
31
|
+
"border-top-left-radius": ["border-radius"],
|
|
32
|
+
"border-top-right-radius": ["border-radius"],
|
|
33
|
+
"border-bottom-right-radius": ["border-radius"],
|
|
34
|
+
"border-bottom-left-radius": ["border-radius"],
|
|
35
|
+
"border-top-width": ["border-top", "border-width", "border"],
|
|
36
|
+
"border-right-width": ["border-right", "border-width", "border"],
|
|
37
|
+
"border-bottom-width": ["border-bottom", "border-width", "border"],
|
|
38
|
+
"border-left-width": ["border-left", "border-width", "border"],
|
|
39
|
+
"border-top-style": ["border-top", "border-style", "border"],
|
|
40
|
+
"border-right-style": ["border-right", "border-style", "border"],
|
|
41
|
+
"border-bottom-style": ["border-bottom", "border-style", "border"],
|
|
42
|
+
"border-left-style": ["border-left", "border-style", "border"],
|
|
43
|
+
"border-top-color": ["border-top", "border-color", "border"],
|
|
44
|
+
"border-right-color": ["border-right", "border-color", "border"],
|
|
45
|
+
"border-bottom-color": ["border-bottom", "border-color", "border"],
|
|
46
|
+
"border-left-color": ["border-left", "border-color", "border"],
|
|
47
|
+
"margin-top": ["margin", "margin-block", "margin-block-start", "margin-block-end"],
|
|
48
|
+
"margin-right": ["margin", "margin-inline", "margin-inline-start", "margin-inline-end"],
|
|
49
|
+
"margin-bottom": ["margin", "margin-block", "margin-block-start", "margin-block-end"],
|
|
50
|
+
"margin-left": ["margin", "margin-inline", "margin-inline-start", "margin-inline-end"],
|
|
51
|
+
"padding-top": ["padding", "padding-block", "padding-block-start", "padding-block-end"],
|
|
52
|
+
"padding-right": ["padding", "padding-inline", "padding-inline-start", "padding-inline-end"],
|
|
53
|
+
"padding-bottom": ["padding", "padding-block", "padding-block-start", "padding-block-end"],
|
|
54
|
+
"padding-left": ["padding", "padding-inline", "padding-inline-start", "padding-inline-end"],
|
|
55
|
+
top: ["inset", "inset-block", "inset-block-start", "inset-block-end"],
|
|
56
|
+
right: ["inset", "inset-inline", "inset-inline-start", "inset-inline-end"],
|
|
57
|
+
bottom: ["inset", "inset-block", "inset-block-start", "inset-block-end"],
|
|
58
|
+
left: ["inset", "inset-inline", "inset-inline-start", "inset-inline-end"],
|
|
59
|
+
"grid-template-columns": ["grid-template", "grid"],
|
|
60
|
+
"grid-template-rows": ["grid-template", "grid"],
|
|
61
|
+
"grid-template-areas": ["grid-template", "grid"],
|
|
62
|
+
"grid-auto-columns": ["grid-auto", "grid"],
|
|
63
|
+
"grid-auto-rows": ["grid-auto", "grid"],
|
|
64
|
+
"grid-auto-flow": ["grid-auto", "grid"],
|
|
65
|
+
"grid-row": ["grid-area", "grid"],
|
|
66
|
+
"grid-column": ["grid-area", "grid"],
|
|
67
|
+
"grid-row-start": ["grid-row", "grid-area", "grid"],
|
|
68
|
+
"grid-row-end": ["grid-row", "grid-area", "grid"],
|
|
69
|
+
"grid-column-start": ["grid-column", "grid-area", "grid"],
|
|
70
|
+
"grid-column-end": ["grid-column", "grid-area", "grid"],
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Longhands edited together by one linked field. The field is blocked when
|
|
74
|
+
* any of them is inline-authored, even if the shorthand itself is not.
|
|
75
|
+
*/
|
|
76
|
+
const LINKED_LONGHANDS = {
|
|
77
|
+
"border-width": ["border-top-width", "border-right-width", "border-bottom-width", "border-left-width"],
|
|
78
|
+
"border-style": ["border-top-style", "border-right-style", "border-bottom-style", "border-left-style"],
|
|
79
|
+
"border-color": ["border-top-color", "border-right-color", "border-bottom-color", "border-left-color"],
|
|
80
|
+
"border-radius": ["border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius"],
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Returns the inline-authored source when `el` carries the property — or a
|
|
84
|
+
* shorthand that sets it — in its `style` attribute. Managed previews can
|
|
85
|
+
* never beat inline styles in the cascade, so a non-null result means the
|
|
86
|
+
* field must present as blocked instead of accepting edits that silently
|
|
87
|
+
* revert (see `verifyPreview`'s "inline-style" conflict).
|
|
88
|
+
*/
|
|
89
|
+
export function inlineAuthoredValue(el, property) {
|
|
90
|
+
if (isLayoutPreview(el, property))
|
|
91
|
+
return null;
|
|
92
|
+
for (const source of [property, ...(SHORTHAND_SOURCES[property] ?? [])]) {
|
|
93
|
+
const value = el.style.getPropertyValue(source).trim();
|
|
94
|
+
if (value)
|
|
95
|
+
return `${source}: ${value}`;
|
|
96
|
+
}
|
|
97
|
+
for (const longhand of LINKED_LONGHANDS[property] ?? []) {
|
|
98
|
+
const value = inlineAuthoredValue(el, longhand);
|
|
99
|
+
if (value)
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
/** First inline-authored source that blocks any of `properties` on any of `elements`. */
|
|
105
|
+
export function inlineBlockedBy(elements, ...properties) {
|
|
106
|
+
for (const property of properties) {
|
|
107
|
+
for (const el of elements) {
|
|
108
|
+
const value = inlineAuthoredValue(el, property);
|
|
109
|
+
if (value)
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
@@ -4,14 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export declare function meaningfulLayoutValue(el: HTMLElement, property: string): string;
|
|
6
6
|
export declare function readAuthoredStyleValue(el: HTMLElement, property: string): string | null;
|
|
7
|
-
/**
|
|
8
|
-
* Returns the inline-authored source when `el` carries the property — or a
|
|
9
|
-
* shorthand that sets it — in its `style` attribute. Managed previews can
|
|
10
|
-
* never beat inline styles in the cascade, so a non-null result means the
|
|
11
|
-
* field must present as blocked instead of accepting edits that silently
|
|
12
|
-
* revert (see `verifyPreview`'s "inline-style" conflict).
|
|
13
|
-
*/
|
|
14
|
-
export declare function inlineAuthoredValue(el: HTMLElement, property: string): string | null;
|
|
15
7
|
export interface LayoutValue {
|
|
16
8
|
property: string;
|
|
17
9
|
authored: string | null;
|
|
@@ -2,7 +2,6 @@ import { getActiveStyleState } from "../shell/styleState.js";
|
|
|
2
2
|
import { getStateStyleValue } from "../shell/stateValue.js";
|
|
3
3
|
import { getElementComputedStyle } from "../runtime/domRealm.js";
|
|
4
4
|
import { getBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
|
|
5
|
-
import { isLayoutPreview } from "./layoutPreviewState.js";
|
|
6
5
|
const DEFAULT_LAYOUT_VALUES = {
|
|
7
6
|
width: "auto",
|
|
8
7
|
height: "auto",
|
|
@@ -48,47 +47,6 @@ export function readAuthoredStyleValue(el, property) {
|
|
|
48
47
|
const inline = el.style.getPropertyValue(property).trim();
|
|
49
48
|
return inline || null;
|
|
50
49
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Shorthands that also author the keyed longhand when present in a `style`
|
|
53
|
-
* attribute. Managed-stylesheet previews lose the cascade to any of these,
|
|
54
|
-
* so fields must treat the longhand as inline-blocked either way.
|
|
55
|
-
*/
|
|
56
|
-
const INLINE_SHORTHAND_SOURCES = {
|
|
57
|
-
"row-gap": ["gap"],
|
|
58
|
-
"column-gap": ["gap"],
|
|
59
|
-
"margin-top": ["margin"],
|
|
60
|
-
"margin-right": ["margin"],
|
|
61
|
-
"margin-bottom": ["margin"],
|
|
62
|
-
"margin-left": ["margin"],
|
|
63
|
-
"padding-top": ["padding"],
|
|
64
|
-
"padding-right": ["padding"],
|
|
65
|
-
"padding-bottom": ["padding"],
|
|
66
|
-
"padding-left": ["padding"],
|
|
67
|
-
top: ["inset"],
|
|
68
|
-
right: ["inset"],
|
|
69
|
-
bottom: ["inset"],
|
|
70
|
-
left: ["inset"],
|
|
71
|
-
};
|
|
72
|
-
/**
|
|
73
|
-
* Returns the inline-authored source when `el` carries the property — or a
|
|
74
|
-
* shorthand that sets it — in its `style` attribute. Managed previews can
|
|
75
|
-
* never beat inline styles in the cascade, so a non-null result means the
|
|
76
|
-
* field must present as blocked instead of accepting edits that silently
|
|
77
|
-
* revert (see `verifyPreview`'s "inline-style" conflict).
|
|
78
|
-
*/
|
|
79
|
-
export function inlineAuthoredValue(el, property) {
|
|
80
|
-
if (isLayoutPreview(el, property))
|
|
81
|
-
return null;
|
|
82
|
-
const direct = el.style.getPropertyValue(property).trim();
|
|
83
|
-
if (direct)
|
|
84
|
-
return `${property}: ${direct}`;
|
|
85
|
-
for (const shorthand of INLINE_SHORTHAND_SOURCES[property] ?? []) {
|
|
86
|
-
const value = el.style.getPropertyValue(shorthand).trim();
|
|
87
|
-
if (value)
|
|
88
|
-
return `${shorthand}: ${value}`;
|
|
89
|
-
}
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
50
|
/**
|
|
93
51
|
* Reads both representations needed by a layout editor. Authored CSS is the
|
|
94
52
|
* editable source of truth; computed CSS is only the browser preview/fallback.
|
|
@@ -7,10 +7,10 @@ export declare function nudgeCssValue(property: string, rawValue: string, direct
|
|
|
7
7
|
/** Returns whether a style field exposes the horizontal drag nudge affordance. */
|
|
8
8
|
export declare function supportsDragNudge(property: string): boolean;
|
|
9
9
|
/**
|
|
10
|
-
* Applies one nudge per
|
|
11
|
-
*
|
|
12
|
-
* value, the first step snaps to the next large-step boundary in the
|
|
13
|
-
* direction.
|
|
10
|
+
* Applies one regular nudge per sixteen horizontal pointer pixels and one large
|
|
11
|
+
* nudge per eight horizontal pointer pixels. When a large drag starts from an
|
|
12
|
+
* off-grid value, the first step snaps to the next large-step boundary in the
|
|
13
|
+
* drag direction.
|
|
14
14
|
*/
|
|
15
15
|
export declare function nudgeCssValueByDrag(property: string, rawValue: string, deltaX: number, large?: boolean, snapToLargeStep?: boolean): string | null;
|
|
16
16
|
/** Returns whether a field has a numeric value that can respond to dragging. */
|