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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import type
|
|
2
|
+
import { type EditTarget } from "../selection/editTarget.js";
|
|
3
3
|
export declare const GRID_PICKER_MAX_COLUMNS = 12;
|
|
4
4
|
export declare const GRID_PICKER_MAX_ROWS = 8;
|
|
5
5
|
export interface GridPickerProps {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
3
|
import { getLayoutValue } from "./layoutValue.js";
|
|
4
|
+
import { inlineBlockedBy } from "./inlineAuthored.js";
|
|
4
5
|
import { setStyles } from "../tokens/editActions.js";
|
|
6
|
+
import { targetElements } from "../selection/editTarget.js";
|
|
7
|
+
import { InlineStyleWarning } from "../ui/InlineStyleWarning.js";
|
|
5
8
|
export const GRID_PICKER_MAX_COLUMNS = 12;
|
|
6
9
|
export const GRID_PICKER_MAX_ROWS = 8;
|
|
7
10
|
function clampDimension(value, max) {
|
|
@@ -96,6 +99,7 @@ export function GridPicker({ domElement: el, editTarget, revision = 0, onAfterEd
|
|
|
96
99
|
const [popoverPosition, setPopoverPosition] = useState(null);
|
|
97
100
|
const pickerRef = useRef(null);
|
|
98
101
|
const triggerRef = useRef(null);
|
|
102
|
+
const blockedBy = inlineBlockedBy(targetElements(editTarget ?? el), "grid-template-columns", "grid-template-rows");
|
|
99
103
|
useEffect(() => {
|
|
100
104
|
setDimensions(readDimensions(el));
|
|
101
105
|
setHovered(null);
|
|
@@ -143,6 +147,8 @@ export function GridPicker({ domElement: el, editTarget, revision = 0, onAfterEd
|
|
|
143
147
|
};
|
|
144
148
|
}, [open]);
|
|
145
149
|
function choose(columns, rows) {
|
|
150
|
+
if (blockedBy)
|
|
151
|
+
return;
|
|
146
152
|
const next = {
|
|
147
153
|
columns: clampDimension(columns, GRID_PICKER_MAX_COLUMNS),
|
|
148
154
|
rows: clampDimension(rows, GRID_PICKER_MAX_ROWS),
|
|
@@ -165,10 +171,10 @@ export function GridPicker({ domElement: el, editTarget, revision = 0, onAfterEd
|
|
|
165
171
|
setOpen(false);
|
|
166
172
|
setHovered(null);
|
|
167
173
|
}
|
|
168
|
-
}, children: [_jsx("button", { type: "button", className: "grid-picker__trigger", "data-test": "layout-grid-picker-trigger", ref: triggerRef, "aria-label": `Grid ${dimensions.columns} by ${dimensions.rows}`, "aria-expanded": open, "aria-haspopup": "dialog", onClick: () => {
|
|
174
|
+
}, children: [_jsx("button", { type: "button", className: "grid-picker__trigger", "data-test": "layout-grid-picker-trigger", ref: triggerRef, "aria-label": `Grid ${dimensions.columns} by ${dimensions.rows}`, "aria-expanded": open, "aria-haspopup": "dialog", disabled: blockedBy !== null, onClick: () => {
|
|
169
175
|
setPopoverPosition(null);
|
|
170
176
|
setOpen((current) => !current);
|
|
171
|
-
}, children: _jsxs("span", { className: "grid-picker__preview", style: gridStyle(dimensions.columns, dimensions.rows), "aria-hidden": "true", children: [previewCells.map(({ column, row }) => (_jsx("span", { className: "grid-picker__preview-cell" }, `${column}-${row}`))), _jsxs("span", { className: "grid-picker__preview-label", children: [dimensions.columns, " \u00D7 ", dimensions.rows] })] }) }), open ? (_jsxs("div", { className: "grid-picker__popover", "data-test": "layout-grid-picker-popover", role: "dialog", "aria-label": "Choose grid size", style: popoverPosition ? {
|
|
177
|
+
}, children: _jsxs("span", { className: "grid-picker__preview", style: gridStyle(dimensions.columns, dimensions.rows), "aria-hidden": "true", children: [previewCells.map(({ column, row }) => (_jsx("span", { className: "grid-picker__preview-cell" }, `${column}-${row}`))), _jsxs("span", { className: "grid-picker__preview-label", children: [dimensions.columns, " \u00D7 ", dimensions.rows] })] }) }), _jsx(InlineStyleWarning, { blockedBy: blockedBy }), open ? (_jsxs("div", { className: "grid-picker__popover", "data-test": "layout-grid-picker-popover", role: "dialog", "aria-label": "Choose grid size", style: popoverPosition ? {
|
|
172
178
|
left: popoverPosition.left,
|
|
173
179
|
top: popoverPosition.top,
|
|
174
180
|
visibility: "visible",
|
|
@@ -23,5 +23,5 @@ const GRID_CONTENT_ALIGNMENT_OPTIONS = [
|
|
|
23
23
|
const GRID_ITEM_ALIGNMENT_OPTIONS = ["normal", "stretch", "start", "end", "center", "self-start", "self-end"];
|
|
24
24
|
export function GridSection({ domElement: el, editTarget, selection, entries, tokenRows, showContainer, showChild, revision = 0, onAfterEdit, }) {
|
|
25
25
|
const [advancedOpen, setAdvancedOpen] = useState(false);
|
|
26
|
-
return (_jsxs(_Fragment, { children: [showContainer ? _jsx("div", { className: "layout__group layout__grid", "data-test": "layout-grid-container", children: _jsxs("div", { className: "layout__grid-primary", children: [_jsxs("div", { className: "layout__grid-picker-column", children: [_jsxs("div", { className: "layout__grid-heading", children: [_jsx("div", { className: "editor__title", children: "Grid" }), _jsx(InspectorPopover, { "data-test": "layout-grid-settings", side: "bottom", align: "end", triggerElement: (_jsx(IconButton, { variant: "quiet", size: "default", label: "Grid settings",
|
|
26
|
+
return (_jsxs(_Fragment, { children: [showContainer ? _jsx("div", { className: "layout__group layout__grid", "data-test": "layout-grid-container", children: _jsxs("div", { className: "layout__grid-primary", children: [_jsxs("div", { className: "layout__grid-picker-column", children: [_jsxs("div", { className: "layout__grid-heading", children: [_jsx("div", { className: "editor__title", children: "Grid" }), _jsx(InspectorPopover, { "data-test": "layout-grid-settings", side: "bottom", align: "end", triggerTooltip: "Grid settings", triggerElement: (_jsx(IconButton, { variant: "quiet", size: "default", label: "Grid settings", "data-active": advancedOpen, "data-test": "layout-grid-settings", children: _jsx(IconSettings, { size: 16, stroke: 1.8, "aria-hidden": "true" }) })), open: advancedOpen, onOpenChange: setAdvancedOpen, children: _jsxs("div", { className: "layout__grid-advanced", "data-test": "layout-grid-advanced", children: [_jsx("div", { className: "editor__title", children: "Advanced grid CSS" }), _jsxs("div", { className: "layout__grid-fields", children: [_jsx(GridValueField, { property: "grid-template-columns", domElement: el, editTarget: editTarget, selection: selection, revision: revision, onAfterEdit: onAfterEdit }), _jsx(GridValueField, { property: "grid-template-rows", domElement: el, editTarget: editTarget, selection: selection, revision: revision, onAfterEdit: onAfterEdit }), _jsx(LayoutDropdown, { property: "grid-auto-flow", options: GRID_AUTO_FLOW_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, revision: revision, onAfterEdit: onAfterEdit }), _jsx(GridValueField, { property: "grid-auto-columns", domElement: el, editTarget: editTarget, selection: selection, revision: revision, onAfterEdit: onAfterEdit }), _jsx(GridValueField, { property: "grid-auto-rows", domElement: el, editTarget: editTarget, selection: selection, revision: revision, onAfterEdit: onAfterEdit })] }), _jsxs("div", { className: "layout__grid-alignment", "data-test": "layout-grid-alignment", children: [_jsx(LayoutDropdown, { property: "justify-content", options: GRID_CONTENT_ALIGNMENT_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, stacked: true, revision: revision, onAfterEdit: onAfterEdit }), _jsx(LayoutDropdown, { property: "align-content", options: GRID_CONTENT_ALIGNMENT_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, stacked: true, revision: revision, onAfterEdit: onAfterEdit }), _jsx(LayoutDropdown, { property: "justify-items", options: GRID_ITEM_ALIGNMENT_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, stacked: true, revision: revision, onAfterEdit: onAfterEdit }), _jsx(LayoutDropdown, { property: "align-items", options: GRID_ITEM_ALIGNMENT_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, stacked: true, revision: revision, onAfterEdit: onAfterEdit })] })] }) })] }), _jsx(GridPicker, { domElement: el, editTarget: editTarget, revision: revision, onAfterEdit: onAfterEdit })] }), _jsxs("div", { className: "layout__grid-gap", "data-test": "layout-grid-gap", children: [_jsx("div", { className: "layout__grid-heading", "data-test": "layout-grid-gap-heading", "aria-hidden": "true", children: _jsx("div", { className: "editor__title" }) }), _jsxs("div", { className: "layout__grid-gap-fields", children: [_jsx(ControlSurface, { className: "layout__spacing-field", "data-test": "layout-grid-row-gap", children: _jsx(GapField, { property: "row-gap", domElement: el, editTarget: editTarget, selection: selection, entries: entries, tokenRows: tokenRows, leading: (_jsx(IconSpacingVertical, { className: "layout__spacing-icon", size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true", "data-test": "layout-spacing-icon-row-gap" })), onAfterEdit: onAfterEdit }) }), _jsx(ControlSurface, { className: "layout__spacing-field", "data-test": "layout-grid-column-gap", children: _jsx(GapField, { property: "column-gap", domElement: el, editTarget: editTarget, selection: selection, entries: entries, tokenRows: tokenRows, leading: (_jsx(IconSpacingHorizontal, { className: "layout__spacing-icon", size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true", "data-test": "layout-spacing-icon-column-gap" })), onAfterEdit: onAfterEdit }) })] })] })] }) }) : null, showChild ? _jsx(GridChildSection, { domElement: el, editTarget: editTarget, revision: revision, onAfterEdit: onAfterEdit }) : null] }));
|
|
27
27
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { FieldRow } from "../ui/FieldRow.js";
|
|
4
4
|
import { TextInput } from "../ui/TextInput.js";
|
|
5
5
|
import { getLayoutValue } from "./layoutValue.js";
|
|
6
6
|
import { setStyle } from "../tokens/editActions.js";
|
|
7
7
|
import { useFieldAtRules } from "../ui/AtRuleContext.js";
|
|
8
|
+
import { InlineStyleWarning } from "../ui/InlineStyleWarning.js";
|
|
9
|
+
import { inlineBlockedBy } from "./inlineAuthored.js";
|
|
8
10
|
const DEFAULT_GRID_VALUES = {
|
|
9
11
|
"grid-template-columns": "none",
|
|
10
12
|
"grid-template-rows": "none",
|
|
@@ -21,6 +23,7 @@ const DEFAULT_GRID_VALUES = {
|
|
|
21
23
|
export function GridValueField({ property, domElement: el, editTarget, selection, revision = 0, onAfterEdit, }) {
|
|
22
24
|
const atRules = useFieldAtRules(property);
|
|
23
25
|
const mixed = selection?.getProperty(property)?.value.kind === "mixed";
|
|
26
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [el], property);
|
|
24
27
|
const readValue = () => {
|
|
25
28
|
const value = getLayoutValue(el, property);
|
|
26
29
|
return value.authored || value.computed || DEFAULT_GRID_VALUES[property] || "";
|
|
@@ -41,6 +44,8 @@ export function GridValueField({ property, domElement: el, editTarget, selection
|
|
|
41
44
|
setDraft(next);
|
|
42
45
|
}
|
|
43
46
|
function commit() {
|
|
47
|
+
if (blockedBy)
|
|
48
|
+
return;
|
|
44
49
|
const next = draftRef.current.trim();
|
|
45
50
|
if (!next) {
|
|
46
51
|
updateDraft(mixed ? "" : value);
|
|
@@ -54,17 +59,17 @@ export function GridValueField({ property, domElement: el, editTarget, selection
|
|
|
54
59
|
function cancel() {
|
|
55
60
|
updateDraft(mixed ? "" : value);
|
|
56
61
|
}
|
|
57
|
-
return (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
return (_jsxs(FieldRow, { label: property, property: property, atRules: atRules, "data-test": `layout-grid-${property}`, children: [_jsx(TextInput, { value: draft, placeholder: mixed ? "Mixed" : undefined, "data-test": `layout-grid-input-${property}`, "aria-label": property, disabled: blockedBy !== null, onChange: (event) => updateDraft(event.target.value), onBlur: () => {
|
|
63
|
+
commit();
|
|
64
|
+
}, onKeyDown: (event) => {
|
|
65
|
+
if (event.key === "Escape") {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
cancel();
|
|
68
|
+
event.currentTarget.blur();
|
|
69
|
+
}
|
|
70
|
+
else if (event.key === "Enter") {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
event.currentTarget.blur();
|
|
73
|
+
}
|
|
74
|
+
} }), _jsx(InlineStyleWarning, { blockedBy: blockedBy })] }));
|
|
70
75
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import type { StyleDeclaration } from "../tokens/editActions.js";
|
|
3
|
+
interface ImageBackgroundEditorProps {
|
|
4
|
+
element: HTMLElement;
|
|
5
|
+
elements: readonly HTMLElement[];
|
|
6
|
+
value: string;
|
|
7
|
+
mixed: boolean;
|
|
8
|
+
sizingValues?: {
|
|
9
|
+
size: string;
|
|
10
|
+
repeat: string;
|
|
11
|
+
};
|
|
12
|
+
onUpload(value: string): boolean;
|
|
13
|
+
onCss(value: string): boolean;
|
|
14
|
+
onSizing(declarations: StyleDeclaration[]): boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Preview uploaded images through the existing workspace style edits. */
|
|
17
|
+
export declare function ImageBackgroundEditor({ element, elements, value, mixed, sizingValues, onUpload, onCss, onSizing }: ImageBackgroundEditorProps): ReactElement;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { IconPhoto, IconUpload } from "@tabler/icons-react";
|
|
4
|
+
import { TokenValueField } from "../tokens/TokenField.js";
|
|
5
|
+
import { ControlSurface } from "../ui/ControlSurface.js";
|
|
6
|
+
import { getActiveDraftChanges } from "../changes/draftChanges.js";
|
|
7
|
+
import { getStateStyleValue } from "../shell/stateValue.js";
|
|
8
|
+
import { Button } from "../ui/Button.js";
|
|
9
|
+
import { InlineStyleWarning } from "../ui/InlineStyleWarning.js";
|
|
10
|
+
import { Select } from "../ui/Select.js";
|
|
11
|
+
import { StatusCallout } from "../ui/StatusCallout.js";
|
|
12
|
+
import { inlineBlockedBy } from "./inlineAuthored.js";
|
|
13
|
+
const SIZING = [
|
|
14
|
+
{ value: "cover", label: "Cover" },
|
|
15
|
+
{ value: "contain", label: "Fit" },
|
|
16
|
+
{ value: "100% 100%", label: "Stretch" },
|
|
17
|
+
{ value: "auto", label: "Original size" },
|
|
18
|
+
{ value: "tile", label: "Tile" },
|
|
19
|
+
];
|
|
20
|
+
function imageUrl(value) {
|
|
21
|
+
const match = /^url\(\s*(?:"((?:\\.|[^"\\])*)"|'((?:\\.|[^'\\])*)'|([^\s"']+))\s*\)$/s.exec(value);
|
|
22
|
+
const url = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
23
|
+
return url?.replace(/\\(["'\\])/g, "$1") ?? null;
|
|
24
|
+
}
|
|
25
|
+
function imageName(src, base) {
|
|
26
|
+
if (src.startsWith("data:"))
|
|
27
|
+
return "Uploaded image";
|
|
28
|
+
try {
|
|
29
|
+
return decodeURIComponent(new URL(src, base).pathname.split("/").pop() || "Background image");
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return "Background image";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Preview uploaded images through the existing workspace style edits. */
|
|
36
|
+
export function ImageBackgroundEditor({ element, elements, value, mixed, sizingValues, onUpload, onCss, onSizing }) {
|
|
37
|
+
const input = useRef(null);
|
|
38
|
+
const reader = useRef(null);
|
|
39
|
+
const request = useRef(0);
|
|
40
|
+
const currentElements = useRef(elements);
|
|
41
|
+
currentElements.current = elements;
|
|
42
|
+
const [pending, setPending] = useState(false);
|
|
43
|
+
const [error, setError] = useState(null);
|
|
44
|
+
const [image, setImage] = useState(null);
|
|
45
|
+
const [sizingDraft, setSizingDraft] = useState(null);
|
|
46
|
+
const src = mixed ? null : imageUrl(value);
|
|
47
|
+
const title = src ? image?.src === src ? image.name : imageName(src, element.ownerDocument.baseURI) : null;
|
|
48
|
+
const computedSize = sizingValues?.size ?? getStateStyleValue(element, "background-size", "auto");
|
|
49
|
+
const computedRepeat = sizingValues?.repeat ?? getStateStyleValue(element, "background-repeat", "repeat");
|
|
50
|
+
const computedSizing = computedRepeat === "repeat" && /^(?:auto|auto auto)$/.test(computedSize) ? "tile"
|
|
51
|
+
: computedSize === "auto auto" ? "auto" : computedSize;
|
|
52
|
+
const sizing = sizingDraft?.element === element && sizingDraft.revision === getActiveDraftChanges().revision ? sizingDraft.value : computedSizing;
|
|
53
|
+
const uploadBlocked = inlineBlockedBy(elements, "background-image", "background-size", "background-repeat", "background-position");
|
|
54
|
+
const sizingBlocked = inlineBlockedBy(elements, "background-size", "background-repeat", "background-position");
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
setPending(false);
|
|
57
|
+
setError(null);
|
|
58
|
+
return () => { request.current++; reader.current?.abort(); };
|
|
59
|
+
}, [element, value]);
|
|
60
|
+
async function upload(file) {
|
|
61
|
+
const id = ++request.current;
|
|
62
|
+
const targets = [...elements];
|
|
63
|
+
const isCurrent = () => request.current === id && currentElements.current.length === targets.length
|
|
64
|
+
&& currentElements.current.every((element, index) => element === targets[index]);
|
|
65
|
+
if (!file.type.startsWith("image/")) {
|
|
66
|
+
setError("Choose an image file.");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (uploadBlocked)
|
|
70
|
+
return;
|
|
71
|
+
setPending(true);
|
|
72
|
+
setError(null);
|
|
73
|
+
try {
|
|
74
|
+
const data = await new Promise((resolve, reject) => {
|
|
75
|
+
const fileReader = new FileReader();
|
|
76
|
+
reader.current = fileReader;
|
|
77
|
+
fileReader.onload = () => typeof fileReader.result === "string" ? resolve(fileReader.result) : reject(new Error("No image data"));
|
|
78
|
+
fileReader.onerror = fileReader.onabort = () => reject(new Error("Cannot read image"));
|
|
79
|
+
fileReader.readAsDataURL(file);
|
|
80
|
+
});
|
|
81
|
+
const probe = new Image();
|
|
82
|
+
probe.src = data;
|
|
83
|
+
await probe.decode();
|
|
84
|
+
if (!isCurrent())
|
|
85
|
+
return;
|
|
86
|
+
if (!onUpload(`url("${data}")`))
|
|
87
|
+
return;
|
|
88
|
+
setImage({ src: data, name: file.name });
|
|
89
|
+
setSizingDraft({ element, revision: getActiveDraftChanges().revision, value: "cover" });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
if (isCurrent())
|
|
93
|
+
setError("This image could not be loaded. Choose another file.");
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
if (isCurrent())
|
|
97
|
+
setPending(false);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function changeSizing(next) {
|
|
101
|
+
if (sizingBlocked)
|
|
102
|
+
return;
|
|
103
|
+
if (!onSizing([
|
|
104
|
+
{ property: "background-size", value: next === "tile" ? "auto" : next },
|
|
105
|
+
{ property: "background-repeat", value: next === "tile" ? "repeat" : "no-repeat" },
|
|
106
|
+
{ property: "background-position", value: "center" },
|
|
107
|
+
]))
|
|
108
|
+
return;
|
|
109
|
+
setSizingDraft({ element, revision: getActiveDraftChanges().revision, value: next });
|
|
110
|
+
}
|
|
111
|
+
return _jsxs("div", { className: "image-background", "data-test": "image-background-editor", children: [_jsx("input", { ref: input, type: "file", accept: "image/*", hidden: true, "data-test": "background-image-upload", "aria-label": "Upload background image", disabled: uploadBlocked !== null || pending, onChange: (event) => {
|
|
112
|
+
const file = event.currentTarget.files?.[0];
|
|
113
|
+
event.currentTarget.value = "";
|
|
114
|
+
if (file)
|
|
115
|
+
void upload(file);
|
|
116
|
+
} }), src ? _jsxs("div", { className: "image-background__grid", children: [_jsxs(Button, { variant: "quiet", className: "image-background__tile", "aria-label": "Replace background image", disabled: uploadBlocked !== null || pending, onClick: () => input.current?.click(), children: [_jsx("img", { className: "image-background__thumb", src: src, alt: "Background image thumbnail", onLoad: () => setImage((previous) => previous?.src === src ? previous : { src, name: imageName(src, element.ownerDocument.baseURI) }), onError: () => setError("The background image could not be loaded.") }), _jsx("span", { className: "image-background__title", title: title ?? undefined, children: title })] }), _jsx(Select, { "aria-label": "Background image sizing", value: sizing, options: SIZING.some((option) => option.value === sizing) ? SIZING : [...SIZING, { value: sizing, label: `Custom (${sizing})` }], disabled: sizingBlocked !== null, onValueChange: changeSizing })] }) : _jsxs(Button, { variant: "secondary", disabled: uploadBlocked !== null || pending, onClick: () => input.current?.click(), children: [pending ? _jsx(IconPhoto, { size: 16, "aria-hidden": "true" }) : _jsx(IconUpload, { size: 16, "aria-hidden": "true" }), pending ? "Loading image…" : mixed ? "Upload image to replace backgrounds" : "Upload image"] }), !src && !mixed && value !== "none" ? _jsx(ControlSurface, { children: _jsx(TokenValueField, { property: "background-image", domElement: element, entries: [], committedValue: value, label: "Background image CSS", blockedBy: inlineBlockedBy(elements, "background-image"), onCommitRaw: (raw) => { if (typeof CSS === "undefined" || CSS.supports("background-image", raw))
|
|
117
|
+
onCss(raw); }, onSelectToken: () => false, onUnlink: onCss }) }) : null, _jsx(InlineStyleWarning, { blockedBy: uploadBlocked ?? sizingBlocked }), error ? _jsx(StatusCallout, { tone: "warning", role: "alert", children: error }) : null] });
|
|
118
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
2
|
export interface LayoutBlockedIndicatorProps {
|
|
3
|
-
blockedBy: string;
|
|
3
|
+
blockedBy: string | null;
|
|
4
4
|
}
|
|
5
5
|
/** Explains why a layout preview cannot override an inline-authored value. */
|
|
6
|
-
export declare function LayoutBlockedIndicator({ blockedBy }: LayoutBlockedIndicatorProps): ReactElement;
|
|
6
|
+
export declare function LayoutBlockedIndicator({ blockedBy }: LayoutBlockedIndicatorProps): ReactElement | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { InlineStyleWarning } from "../ui/InlineStyleWarning.js";
|
|
3
3
|
/** Explains why a layout preview cannot override an inline-authored value. */
|
|
4
4
|
export function LayoutBlockedIndicator({ blockedBy }) {
|
|
5
|
-
return (_jsx(
|
|
5
|
+
return (_jsx(InlineStyleWarning, { blockedBy: blockedBy, dataTest: "layout-combo-blocked", tooltipDataTest: "layout-combo-blocked-tooltip", className: "layout-combo-blocked", symbolClassName: "layout-combo-blocked__symbol" }));
|
|
6
6
|
}
|
|
@@ -4,10 +4,12 @@ import { setStyle } from "../tokens/editActions.js";
|
|
|
4
4
|
import { completeCssValue } from "./completeCssValue.js";
|
|
5
5
|
import { nudgeCssValue } from "./nudgeValue.js";
|
|
6
6
|
import { valuePolicyFor } from "./valuePolicy.js";
|
|
7
|
-
import {
|
|
7
|
+
import { meaningfulLayoutValue } from "./layoutValue.js";
|
|
8
|
+
import { inlineBlockedBy } from "./inlineAuthored.js";
|
|
8
9
|
import { LayoutBlockedIndicator } from "./LayoutBlockedIndicator.js";
|
|
9
10
|
import { Select } from "../ui/Select.js";
|
|
10
11
|
import { TextInput } from "../ui/TextInput.js";
|
|
12
|
+
import { cx } from "../ui/classNames.js";
|
|
11
13
|
import { formatInspectorLabel } from "../ui/labels.js";
|
|
12
14
|
import { AtRuleIndicator, useFieldAtRules } from "../ui/AtRuleContext.js";
|
|
13
15
|
import { getCanvasProjectionAcknowledgementVersion, getCanvasProjectionStatus, subscribeCanvasProjectionAcknowledgements, } from "../canvas/projection.js";
|
|
@@ -58,7 +60,7 @@ export function LayoutComboField(props) {
|
|
|
58
60
|
// blocked (with the reason) instead of accepting edits that silently snap
|
|
59
61
|
// back to the computed value on the next revision. Recomputed every render
|
|
60
62
|
// so external style-attribute edits update the field without a remount.
|
|
61
|
-
const blockedBy =
|
|
63
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [el], property);
|
|
62
64
|
function commit(value) {
|
|
63
65
|
if (blockedBy)
|
|
64
66
|
return;
|
|
@@ -124,10 +126,10 @@ export function LayoutComboField(props) {
|
|
|
124
126
|
draftDirtyRef.current = true;
|
|
125
127
|
setCustomValue(e.target.value);
|
|
126
128
|
}, onBlur: handleCustomApply, onKeyDown: handleCustomKeyDown }));
|
|
127
|
-
return (_jsxs("span", { className:
|
|
129
|
+
return (_jsxs("span", { className: cx("layout-combo", compact && "layout-combo--compact", blockedBy && "layout-combo--blocked"), "data-test": "layout-combo", "data-property": property, children: [inputOnly ? customInput : (_jsxs(_Fragment, { children: [_jsx(Select, { compact: compact, appearance: appearance, "data-test": `layout-combo-select-${property}`, value: selectValue, options: [
|
|
128
130
|
...presets.map((p) => ({ value: p, label: formatInspectorLabel(p) })),
|
|
129
131
|
{ value: CUSTOM_KEY, label: "Custom…" },
|
|
130
|
-
], onValueChange: handleSelectChange, disabled: blockedBy !== null }), showCustom || alwaysShowInput ? (_jsx("span", { className: "layout-combo__custom", children: customInput })) : null] })), _jsx(AtRuleIndicator, { atRules: atRules }),
|
|
132
|
+
], onValueChange: handleSelectChange, disabled: blockedBy !== null }), showCustom || alwaysShowInput ? (_jsx("span", { className: "layout-combo__custom", children: customInput })) : null] })), _jsx(AtRuleIndicator, { atRules: atRules }), _jsx(LayoutBlockedIndicator, { blockedBy: blockedBy })] }));
|
|
131
133
|
}
|
|
132
134
|
function arrowDirection(key) {
|
|
133
135
|
if (key === "ArrowUp" || key === "ArrowRight")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
3
|
import { setStyle } from "../tokens/editActions.js";
|
|
4
4
|
import { FieldRow } from "../ui/FieldRow.js";
|
|
@@ -6,11 +6,14 @@ import { Select } from "../ui/Select.js";
|
|
|
6
6
|
import { formatInspectorLabel } from "../ui/labels.js";
|
|
7
7
|
import { getStateStyleValue } from "../shell/stateValue.js";
|
|
8
8
|
import { useFieldAtRules } from "../ui/AtRuleContext.js";
|
|
9
|
+
import { InlineStyleWarning } from "../ui/InlineStyleWarning.js";
|
|
10
|
+
import { inlineBlockedBy } from "./inlineAuthored.js";
|
|
9
11
|
export function LayoutDropdown(props) {
|
|
10
12
|
const { property, options, domElement: el, editTarget, selection, stacked, revision = 0, onAfterEdit } = props;
|
|
11
13
|
const atRules = useFieldAtRules(property);
|
|
12
14
|
const selectedProperty = selection?.getProperty(property);
|
|
13
15
|
const mixed = selectedProperty?.value.kind === "mixed";
|
|
16
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [el], property);
|
|
14
17
|
const [value, setValue] = useState(() => getStateStyleValue(el, property, options[0]));
|
|
15
18
|
useEffect(() => {
|
|
16
19
|
try {
|
|
@@ -21,7 +24,7 @@ export function LayoutDropdown(props) {
|
|
|
21
24
|
}
|
|
22
25
|
}, [el, property, options, revision]);
|
|
23
26
|
function handleChange(next) {
|
|
24
|
-
if (next === "mixed")
|
|
27
|
+
if (next === "mixed" || blockedBy)
|
|
25
28
|
return;
|
|
26
29
|
setValue(next);
|
|
27
30
|
setStyle(editTarget ?? el, property, next);
|
|
@@ -30,5 +33,5 @@ export function LayoutDropdown(props) {
|
|
|
30
33
|
const allOptions = mixed
|
|
31
34
|
? ["mixed", ...options]
|
|
32
35
|
: value && !options.includes(value) ? [...options, value] : options;
|
|
33
|
-
return (
|
|
36
|
+
return (_jsxs(FieldRow, { label: property, property: property, atRules: atRules, className: stacked ? "field-row--stacked" : undefined, "data-test": "layout-dropdown", children: [_jsx(Select, { "data-test": `layout-select-${property}`, value: mixed ? "mixed" : value, options: allOptions.map((opt) => ({ value: opt, label: formatInspectorLabel(opt) })), onValueChange: handleChange, disabled: blockedBy !== null }), _jsx(InlineStyleWarning, { blockedBy: blockedBy })] }));
|
|
34
37
|
}
|
|
@@ -9,6 +9,7 @@ import { AspectRatioField } from "./AspectRatioField.js";
|
|
|
9
9
|
import { PositionInsets } from "./PositionInsets.js";
|
|
10
10
|
import { GridSection } from "./GridSection.js";
|
|
11
11
|
import { meaningfulLayoutValue } from "./layoutValue.js";
|
|
12
|
+
import { inlineBlockedBy } from "./inlineAuthored.js";
|
|
12
13
|
import { setStyle, setStyles } from "../tokens/editActions.js";
|
|
13
14
|
import { IconButton } from "../ui/IconButton.js";
|
|
14
15
|
import { InspectorPopover } from "../ui/InspectorPopover.js";
|
|
@@ -19,6 +20,8 @@ import { formatInspectorLabel } from "../ui/labels.js";
|
|
|
19
20
|
import { getElementComputedStyle } from "../runtime/domRealm.js";
|
|
20
21
|
import { FieldRow } from "../ui/FieldRow.js";
|
|
21
22
|
import { ControlSurface } from "../ui/ControlSurface.js";
|
|
23
|
+
import { Tooltip } from "../ui/Tooltip.js";
|
|
24
|
+
import { inlineStyleWarningContent } from "../ui/InlineStyleWarning.js";
|
|
22
25
|
import { getNudgeUiTokenEntries } from "../runtime/runtimeConfig.js";
|
|
23
26
|
import { getCanvasProjectionAcknowledgementVersion, subscribeCanvasProjectionAcknowledgements, } from "../canvas/projection.js";
|
|
24
27
|
const DISPLAY_OPTIONS = ["block", "inline", "inline-block", "flex", "inline-flex", "grid", "inline-grid", "none", "contents"];
|
|
@@ -106,7 +109,7 @@ export function LayoutSection(props) {
|
|
|
106
109
|
const showPositionInsets = selection
|
|
107
110
|
? selection.supportsRole("inset-position")
|
|
108
111
|
: position === "absolute" || position === "fixed";
|
|
109
|
-
return (_jsxs("div", { className: "editor", "data-test": "layout-section", children: [_jsx("div", { className: "editor__title", children: "Layout" }), _jsxs("div", { className: "layout", children: [_jsxs("div", { className: "layout__basic", children: [_jsx(SizeSection, { domElement: el, editTarget: editTarget, selection: selection, entries: allEntries, tokenRows: tokenRows, revision: computedRevision, onAfterEdit: notifyAfterEdit }), _jsxs("div", { className: "layout__tool-row", children: [_jsx(LayoutDropdown, { property: "display", options: DISPLAY_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit }), _jsx(LayoutDropdown, { property: "position", options: POSITION_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit })] })] }), isFlexContainer ? (_jsxs("div", { className: "layout__group", "data-test": "layout-flex-container", children: [
|
|
112
|
+
return (_jsxs("div", { className: "editor", "data-test": "layout-section", children: [_jsx("div", { className: "editor__title", children: "Layout" }), _jsxs("div", { className: "layout", children: [_jsxs("div", { className: "layout__basic", children: [_jsx(SizeSection, { domElement: el, editTarget: editTarget, selection: selection, entries: allEntries, tokenRows: tokenRows, revision: computedRevision, onAfterEdit: notifyAfterEdit }), _jsxs("div", { className: "layout__tool-row", children: [_jsx(LayoutDropdown, { property: "display", options: DISPLAY_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit }), _jsx(LayoutDropdown, { property: "position", options: POSITION_OPTIONS, domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit })] })] }), isFlexContainer ? (_jsxs("div", { className: "layout__group", "data-test": "layout-flex-container", children: [_jsxs("div", { className: "layout__flex-heading", children: [_jsx("div", { className: "editor__title", children: "Flex" }), _jsx(FlexSettingsMenu, { domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit })] }), _jsxs("div", { className: "layout__flex-lower", children: [mixedFlexDirection ? (_jsx("div", { className: "layout__alignment-grid", "data-test": "layout-flex-axis-mixed", children: "Choose a shared flex direction to edit physical alignment." })) : (_jsx(FlexAlignmentGrid, { domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit })), _jsxs("div", { className: "layout__gap-column", "data-test": "layout-gap", children: [_jsxs("div", { className: "layout__direction-tools", children: [_jsx(FlexDirectionControl, { domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit }), _jsx(FlexWrapToggle, { domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit })] }), _jsxs("div", { className: "layout__gap-fields", children: [_jsxs("div", { className: "layout__spacing-primary", children: [_jsx(FlexGapField, { property: relevantGap, domElement: el, editTarget: editTarget, selection: selection, entries: allEntries, tokenRows: tokenRows, onAfterEdit: notifyAfterEdit }), _jsx(FlexDistributionControl, { domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit })] }), isFlexWrapped ? (_jsx(FlexGapField, { property: lineGap, domElement: el, editTarget: editTarget, selection: selection, entries: allEntries, tokenRows: tokenRows, onAfterEdit: notifyAfterEdit })) : null] })] })] })] })) : null, isFlexChild ? (_jsxs("div", { className: "layout__group", "data-test": "layout-flex-child", children: [_jsx("div", { className: "editor__title", children: "Flex Child" }), _jsxs("div", { className: "layout__flex-child-fields", children: [_jsx(FlexChildValueField, { label: "Grow", property: "flex-grow", presets: FLEX_GROW_PRESETS, domElement: el, editTarget: editTarget, selection: selection, inputOnly: true, revision: computedRevision, onAfterEdit: notifyAfterEdit }), _jsx(FlexChildValueField, { label: "Shrink", property: "flex-shrink", presets: FLEX_SHRINK_PRESETS, domElement: el, editTarget: editTarget, selection: selection, inputOnly: true, revision: computedRevision, onAfterEdit: notifyAfterEdit }), _jsx(FlexChildValueField, { label: "Basis", property: "flex-basis", presets: FLEX_BASIS_PRESETS, domElement: el, editTarget: editTarget, selection: selection, inputOnly: true, revision: computedRevision, onAfterEdit: notifyAfterEdit }), _jsx(FlexChildSettingsMenu, { domElement: el, editTarget: editTarget, selection: selection, revision: computedRevision, onAfterEdit: notifyAfterEdit })] })] })) : null, isGridContainer || isGridChild ? (_jsx(GridSection, { domElement: el, showContainer: isGridContainer, showChild: isGridChild, editTarget: editTarget, selection: selection, entries: allEntries, tokenRows: tokenRows, revision: computedRevision, onAfterEdit: notifyAfterEdit })) : null, showPositionInsets ? (_jsx(PositionInsets, { domElement: el, editTarget: editTarget, selection: selection, entries: allEntries, tokenRows: tokenRows, revision: computedRevision, onAfterEdit: notifyAfterEdit })) : null] })] }));
|
|
110
113
|
}
|
|
111
114
|
function SizeSection({ domElement: el, editTarget, selection, entries, tokenRows, revision, onAfterEdit }) {
|
|
112
115
|
const [expanded, setExpanded] = useState(false);
|
|
@@ -125,14 +128,14 @@ function SizeSection({ domElement: el, editTarget, selection, entries, tokenRows
|
|
|
125
128
|
? _jsx(IconLetterH, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
|
|
126
129
|
: undefined, onAfterEdit: onAfterEdit }) }));
|
|
127
130
|
}
|
|
128
|
-
return (_jsx("div", { className: "layout__size", "data-test": "layout-size", "data-expanded": expanded ? "true" : "false", children: _jsxs("div", { className: "layout__size-grid", children: [_jsx(FieldRow, { label: "Width", hideLabel: true, "data-test": "layout-size-width", className: "layout__size-cell layout__size-cell--width", children: renderTokenField("width", SIZE_PRESETS) }), _jsx(FieldRow, { label: "Height", hideLabel: true, "data-test": "layout-size-height", className: "layout__size-cell layout__size-cell--height", children: renderTokenField("height", SIZE_PRESETS) }), _jsx(IconButton, { className: "layout__size-cell layout__size-cell--toggle", variant: "quiet", size: "default", "data-test": expanded ? "layout-size-collapse" : "layout-size-expand", "aria-label": expanded ? "Collapse Size Fields" : "Expand Size Fields", label: expanded ? "Collapse Size Fields" : "Expand Size Fields",
|
|
131
|
+
return (_jsx("div", { className: "layout__size", "data-test": "layout-size", "data-expanded": expanded ? "true" : "false", children: _jsxs("div", { className: "layout__size-grid", children: [_jsx(FieldRow, { label: "Width", hideLabel: true, "data-test": "layout-size-width", className: "layout__size-cell layout__size-cell--width", children: renderTokenField("width", SIZE_PRESETS) }), _jsx(FieldRow, { label: "Height", hideLabel: true, "data-test": "layout-size-height", className: "layout__size-cell layout__size-cell--height", children: renderTokenField("height", SIZE_PRESETS) }), _jsx(Tooltip, { children: _jsx(IconButton, { className: "layout__size-cell layout__size-cell--toggle", variant: "quiet", size: "default", "data-test": expanded ? "layout-size-collapse" : "layout-size-expand", "aria-label": expanded ? "Collapse Size Fields" : "Expand Size Fields", label: expanded ? "Collapse Size Fields" : "Expand Size Fields", onClick: () => setExpanded(!expanded), children: expanded ? (_jsx(IconArrowsMinimize, { size: 16, stroke: 1.8, "aria-hidden": "true" })) : (_jsx(IconArrowsMaximize, { size: 16, stroke: 1.8, "aria-hidden": "true" })) }) }), expanded && fields.slice(2).map(({ property, presets }) => (_jsx(FieldRow, { label: property, "data-test": `layout-size-${property}`, className: `layout__size-cell layout__size-cell--${property}`, children: renderTokenField(property, presets) }, property))), expanded && (_jsx(AspectRatioField, { className: "layout__size-cell layout__size-cell--aspect-ratio", domElement: el, editTarget: editTarget, selection: selection, entries: entries, tokenRow: tokenRows.find((row) => row.property === "aspect-ratio") ?? null, revision: revision, onAfterEdit: onAfterEdit }))] }) }));
|
|
129
132
|
}
|
|
130
133
|
function FlexChildValueField({ label, property, presets, inputOnly = false, domElement, editTarget, selection, revision = 0, onAfterEdit }) {
|
|
131
134
|
return (_jsx(FieldRow, { label: label, className: "layout__flex-child-field", children: _jsx(LayoutComboField, { property: property, presets: presets, domElement: domElement, editTarget: editTarget, selection: selection, inputOnly: inputOnly, revision: revision, onAfterEdit: onAfterEdit }) }));
|
|
132
135
|
}
|
|
133
136
|
function FlexChildSettingsMenu({ domElement, editTarget, selection, revision = 0, onAfterEdit }) {
|
|
134
137
|
const [open, setOpen] = useState(false);
|
|
135
|
-
return (_jsx(InspectorPopover, { "data-test": "layout-flex-child-settings", side: "left", align: "start", triggerElement: (_jsx(IconButton, { variant: "quiet", size: "default", label: "Flex child settings",
|
|
138
|
+
return (_jsx(InspectorPopover, { "data-test": "layout-flex-child-settings", side: "left", align: "start", triggerTooltip: "Flex child settings", triggerElement: (_jsx(IconButton, { variant: "quiet", size: "default", label: "Flex child settings", "data-test": "layout-flex-child-settings", children: _jsx(IconSettings, { size: 16, stroke: 1.8, "aria-hidden": "true" }) })), open: open, onOpenChange: setOpen, children: _jsxs("div", { className: "layout__flex-child-settings", "data-test": "layout-flex-child-settings-content", children: [_jsx("div", { className: "layout__flex-child-settings-title", children: "Flex Child Settings" }), _jsx(LayoutDropdown, { property: "align-self", options: ALIGN_SELF_OPTIONS, domElement: domElement, editTarget: editTarget, selection: selection, stacked: true, revision: revision, onAfterEdit: onAfterEdit }), _jsx(FlexChildValueField, { label: "Order", property: "order", presets: ORDER_PRESETS, domElement: domElement, editTarget: editTarget, selection: selection, revision: revision, onAfterEdit: onAfterEdit })] }) }));
|
|
136
139
|
}
|
|
137
140
|
function FlexGapField({ property, domElement, editTarget, selection, entries, tokenRows, onAfterEdit }) {
|
|
138
141
|
const GapIcon = property === "column-gap" ? IconSpacingHorizontal : IconSpacingVertical;
|
|
@@ -145,14 +148,17 @@ function FlexDirectionControl({ domElement, editTarget, selection, revision = 0,
|
|
|
145
148
|
const sharedDirection = selectedDirection?.kind === "common" ? selectedDirection.value : direction;
|
|
146
149
|
const orientation = sharedDirection.startsWith("column") ? "column" : "row";
|
|
147
150
|
const reverse = sharedDirection.endsWith("-reverse");
|
|
151
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [domElement], "flex-direction");
|
|
148
152
|
function selectDirection(next) {
|
|
153
|
+
if (blockedBy)
|
|
154
|
+
return;
|
|
149
155
|
if (!FLEX_DIRECTION_OPTIONS.includes(next))
|
|
150
156
|
return;
|
|
151
157
|
setDirection(next);
|
|
152
158
|
setStyle(editTarget ?? domElement, "flex-direction", next);
|
|
153
159
|
onAfterEdit?.();
|
|
154
160
|
}
|
|
155
|
-
|
|
161
|
+
const control = (_jsx(SegmentedControl, { value: mixed ? null : orientation, "aria-label": "Flex direction", disabled: blockedBy !== null, options: [
|
|
156
162
|
{
|
|
157
163
|
value: "row",
|
|
158
164
|
label: "Set Flex Direction To Row",
|
|
@@ -166,6 +172,7 @@ function FlexDirectionControl({ domElement, editTarget, selection, revision = 0,
|
|
|
166
172
|
icon: (_jsx("svg", { viewBox: "0 0 16 16", width: "16", height: "16", "aria-hidden": "true", children: _jsx("path", { d: "M8 2v11M4 9l4 4 4-4", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "var(--icon-stroke-width)" }) })),
|
|
167
173
|
},
|
|
168
174
|
], onChange: (next) => selectDirection(`${next}${!mixed && reverse ? "-reverse" : ""}`) }));
|
|
175
|
+
return _jsx(Tooltip, { content: inlineStyleWarningContent(blockedBy), disabled: blockedBy === null, children: control });
|
|
169
176
|
}
|
|
170
177
|
function FlexWrapToggle({ domElement, editTarget, selection, revision = 0, onAfterEdit }) {
|
|
171
178
|
const [wrap] = useComputedLayoutValue(domElement, "flex-wrap", "nowrap", revision);
|
|
@@ -173,11 +180,14 @@ function FlexWrapToggle({ domElement, editTarget, selection, revision = 0, onAft
|
|
|
173
180
|
const mixed = selectedWrap?.kind === "mixed";
|
|
174
181
|
const sharedWrap = selectedWrap?.kind === "common" ? selectedWrap.value : wrap;
|
|
175
182
|
const isWrapped = !mixed && sharedWrap !== "nowrap";
|
|
183
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [domElement], "flex-wrap");
|
|
176
184
|
function toggleWrap() {
|
|
185
|
+
if (blockedBy)
|
|
186
|
+
return;
|
|
177
187
|
setStyle(editTarget ?? domElement, "flex-wrap", isWrapped ? "nowrap" : "wrap");
|
|
178
188
|
onAfterEdit?.();
|
|
179
189
|
}
|
|
180
|
-
return (_jsx(IconButton, { variant: "secondary", size: "default", "data-active": isWrapped, "data-test": "layout-flex-wrap-toggle", label: mixed ? "Set Flex Wrap" : isWrapped ? "Disable Flex Wrap" : "Enable Flex Wrap", "aria-pressed": mixed ? "mixed" : isWrapped, onClick: toggleWrap, children: _jsx(IconTextWrap, { size: 16, stroke: 1.8, "aria-hidden": "true" }) }));
|
|
190
|
+
return (_jsx(Tooltip, { content: inlineStyleWarningContent(blockedBy), children: _jsx(IconButton, { variant: "secondary", size: "default", "data-active": isWrapped, "data-test": "layout-flex-wrap-toggle", label: mixed ? "Set Flex Wrap" : isWrapped ? "Disable Flex Wrap" : "Enable Flex Wrap", "aria-pressed": mixed ? "mixed" : isWrapped, disabled: blockedBy !== null, onClick: toggleWrap, children: _jsx(IconTextWrap, { size: 16, stroke: 1.8, "aria-hidden": "true" }) }) }));
|
|
181
191
|
}
|
|
182
192
|
function FlexSettingsMenu({ domElement, editTarget, selection, revision = 0, onAfterEdit }) {
|
|
183
193
|
const [direction] = useComputedLayoutValue(domElement, "flex-direction", "row", revision);
|
|
@@ -199,6 +209,7 @@ function FlexSettingsMenu({ domElement, editTarget, selection, revision = 0, onA
|
|
|
199
209
|
const reverseDirection = sharedDirection.endsWith("-reverse")
|
|
200
210
|
? orientation
|
|
201
211
|
: `${orientation}-reverse`;
|
|
212
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [domElement], "flex-direction", "flex-wrap", "align-content", "align-items");
|
|
202
213
|
const items = [
|
|
203
214
|
...(mixedDirection ? [] : [{
|
|
204
215
|
value: `flex-direction:${reverseDirection}`,
|
|
@@ -237,6 +248,8 @@ function FlexSettingsMenu({ domElement, editTarget, selection, revision = 0, onA
|
|
|
237
248
|
},
|
|
238
249
|
];
|
|
239
250
|
function selectSetting(setting) {
|
|
251
|
+
if (blockedBy)
|
|
252
|
+
return;
|
|
240
253
|
const separator = setting.indexOf(":");
|
|
241
254
|
if (separator < 0)
|
|
242
255
|
return;
|
|
@@ -246,7 +259,7 @@ function FlexSettingsMenu({ domElement, editTarget, selection, revision = 0, onA
|
|
|
246
259
|
setOpen(false);
|
|
247
260
|
onAfterEdit?.();
|
|
248
261
|
}
|
|
249
|
-
return (_jsx(PopoverListbox, { query: "", value: null, open: open, triggerElement: (_jsx(IconButton, { variant: "quiet", size: "default", label: "Flex settings", "data-test": "layout-flex-settings", children: _jsx(IconSettings, { size: 16, stroke: 1.8, "aria-hidden": "true" }) })), triggerDataTest: "layout-flex-settings", triggerAriaLabel: "Flex settings", items: items.map((item) => ({
|
|
262
|
+
return (_jsx(PopoverListbox, { query: "", value: null, open: open, triggerElement: (_jsx(IconButton, { variant: "quiet", size: "default", label: "Flex settings", "data-test": "layout-flex-settings", children: _jsx(IconSettings, { size: 16, stroke: 1.8, "aria-hidden": "true" }) })), triggerDataTest: "layout-flex-settings", triggerAriaLabel: "Flex settings", triggerTooltip: inlineStyleWarningContent(blockedBy), disabled: blockedBy !== null, items: items.map((item) => ({
|
|
250
263
|
value: item.value,
|
|
251
264
|
label: item.label,
|
|
252
265
|
trailing: item.trailing,
|
|
@@ -254,22 +267,25 @@ function FlexSettingsMenu({ domElement, editTarget, selection, revision = 0, onA
|
|
|
254
267
|
"data-test": item["data-test"],
|
|
255
268
|
})), onQueryChange: () => undefined, onOpenChange: setOpen, onSelect: selectSetting }));
|
|
256
269
|
}
|
|
257
|
-
function FlexDistributionControl({ domElement, editTarget, revision = 0, onAfterEdit }) {
|
|
270
|
+
function FlexDistributionControl({ domElement, editTarget, selection, revision = 0, onAfterEdit }) {
|
|
258
271
|
const [computedJustify] = useComputedLayoutValue(domElement, "justify-content", "flex-start", revision);
|
|
259
272
|
const justify = normalizeFlexJustify(computedJustify);
|
|
260
273
|
const lastPlacement = useRef("flex-start");
|
|
261
274
|
const distributed = FLEX_DISTRIBUTION_OPTIONS.includes(justify);
|
|
262
275
|
const [open, setOpen] = useState(false);
|
|
276
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [domElement], "justify-content");
|
|
263
277
|
useEffect(() => {
|
|
264
278
|
if (FLEX_ALIGNMENT_OPTIONS.includes(justify))
|
|
265
279
|
lastPlacement.current = justify;
|
|
266
280
|
}, [justify]);
|
|
267
281
|
function selectDistribution(next) {
|
|
282
|
+
if (blockedBy)
|
|
283
|
+
return;
|
|
268
284
|
const value = next === "packed" ? lastPlacement.current : next;
|
|
269
285
|
setStyle(editTarget ?? domElement, "justify-content", value);
|
|
270
286
|
onAfterEdit?.();
|
|
271
287
|
}
|
|
272
|
-
return (_jsx(PopoverListbox, { className: "layout__distribution-control", query: "", value: distributed ? justify : "packed", open: open, triggerElement: (_jsx(IconButton, { variant: "quiet", size: "default", label: "Item distribution",
|
|
288
|
+
return (_jsx(PopoverListbox, { className: "layout__distribution-control", query: "", value: distributed ? justify : "packed", open: open, triggerElement: (_jsx(IconButton, { variant: "quiet", size: "default", label: "Item distribution", "data-active": distributed, "data-test": "layout-flex-distribution", children: _jsx(IconAdjustmentsHorizontal, { size: 16, stroke: 1.8, "aria-hidden": "true" }) })), triggerDataTest: "layout-flex-distribution", triggerAriaLabel: "Item distribution", triggerTooltip: inlineStyleWarningContent(blockedBy), disabled: blockedBy !== null, items: [
|
|
273
289
|
{ value: "packed", label: "Keep grouped" },
|
|
274
290
|
{ value: "space-between", label: "Spread between" },
|
|
275
291
|
{ value: "space-around", label: "Spread around" },
|
|
@@ -285,7 +301,7 @@ function FlexDistributionControl({ domElement, editTarget, revision = 0, onAfter
|
|
|
285
301
|
setOpen(false);
|
|
286
302
|
} }));
|
|
287
303
|
}
|
|
288
|
-
function FlexAlignmentGrid({ domElement, editTarget, revision = 0, onAfterEdit }) {
|
|
304
|
+
function FlexAlignmentGrid({ domElement, editTarget, selection, revision = 0, onAfterEdit }) {
|
|
289
305
|
const [direction] = useComputedLayoutValue(domElement, "flex-direction", "row", revision);
|
|
290
306
|
const [computedJustify, setJustify] = useComputedLayoutValue(domElement, "justify-content", "flex-start", revision);
|
|
291
307
|
const [computedAlign, setAlign] = useComputedLayoutValue(domElement, "align-items", "stretch", revision);
|
|
@@ -294,6 +310,7 @@ function FlexAlignmentGrid({ domElement, editTarget, revision = 0, onAfterEdit }
|
|
|
294
310
|
const isColumn = direction.startsWith("column");
|
|
295
311
|
const isReverse = direction.endsWith("-reverse");
|
|
296
312
|
const distributed = FLEX_DISTRIBUTION_OPTIONS.includes(justify);
|
|
313
|
+
const blockedBy = inlineBlockedBy(selection?.domElements ?? [domElement], "justify-content", "align-items");
|
|
297
314
|
// The grid represents physical positions in the parent. For row flex
|
|
298
315
|
// containers, justify-content runs horizontally; for column flex
|
|
299
316
|
// containers, it runs vertically, so the two grid axes need to transpose.
|
|
@@ -304,6 +321,8 @@ function FlexAlignmentGrid({ domElement, editTarget, revision = 0, onAfterEdit }
|
|
|
304
321
|
? [...FLEX_ALIGNMENT_OPTIONS].reverse()
|
|
305
322
|
: FLEX_ALIGNMENT_OPTIONS;
|
|
306
323
|
function selectAlignment(nextJustify, nextAlign) {
|
|
324
|
+
if (blockedBy)
|
|
325
|
+
return;
|
|
307
326
|
// Keep an explicit distribution choice when the grid is used to change
|
|
308
327
|
// the cross-axis alignment. The distribution control owns values such as
|
|
309
328
|
// `space-between`; a grid click should not silently replace them with the
|
|
@@ -317,12 +336,14 @@ function FlexAlignmentGrid({ domElement, editTarget, revision = 0, onAfterEdit }
|
|
|
317
336
|
]);
|
|
318
337
|
onAfterEdit?.();
|
|
319
338
|
}
|
|
320
|
-
|
|
339
|
+
const grid = (_jsxs("div", { className: "layout__alignment-grid", role: "group", "aria-label": "Place flex items", "data-direction": isColumn ? "column" : "row", "data-justify": justify, "data-align": align, "data-distributed": distributed, children: [distributed ? (_jsxs("span", { className: "layout__distribution-preview", "aria-hidden": "true", children: [_jsx("span", {}), _jsx("span", {}), _jsx("span", {})] })) : null, rowValues.flatMap((rowValue) => columnValues.map((columnValue) => {
|
|
321
340
|
const justifyValue = isColumn ? rowValue : columnValue;
|
|
322
341
|
const alignValue = isColumn ? columnValue : rowValue;
|
|
323
342
|
const active = justify === justifyValue && align === alignValue;
|
|
324
|
-
|
|
343
|
+
const label = formatInspectorLabel(`Align ${alignValue.replace("flex-", "")} And Distribute ${justifyValue.replace("flex-", "")}`);
|
|
344
|
+
return (_jsx(Tooltip, { children: _jsx(IconButton, { size: "default", variant: "quiet", className: "layout__alignment-button", "data-active": active, "data-test": `layout-align-${alignValue}-${justifyValue}`, label: label, "aria-pressed": active, disabled: blockedBy !== null, onClick: () => selectAlignment(justifyValue, alignValue), children: _jsx("span", {}) }) }, `${alignValue}-${justifyValue}`));
|
|
325
345
|
}))] }));
|
|
346
|
+
return _jsx(Tooltip, { content: inlineStyleWarningContent(blockedBy), disabled: blockedBy === null, children: grid });
|
|
326
347
|
}
|
|
327
348
|
function normalizeFlexJustify(value) {
|
|
328
349
|
// `normal` is the computed initial value, but behaves as `flex-start` for
|