Many subtle improvements to the UI design system (#1537)

This commit is contained in:
Keavon Chambers
2023-12-28 04:35:20 -08:00
committed by GitHub
parent 34f952bad1
commit 96b5d7b520
62 changed files with 326 additions and 521 deletions
+1 -6
View File
@@ -189,11 +189,6 @@
display: block;
}
.sharp-right-corners.sharp-right-corners.sharp-right-corners.sharp-right-corners {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.layout-row,
.layout-col {
.scrollable-x,
@@ -265,7 +260,7 @@
.popover-button,
.color-button > button,
.color-picker .preset-color,
.working-colors-button .swatch > button,
.working-colors-input .swatch > button,
.radio-input button,
.menu-list,
.menu-list-button .entry,
@@ -537,7 +537,7 @@
width: 208px;
height: 32px;
border-radius: 2px;
border: 1px solid var(--color-0-black);
border: 1px solid var(--color-1-nearblack);
box-sizing: border-box;
overflow: hidden;
@@ -41,13 +41,13 @@
<div class="widget-layout details">
<div class="widget-span row"><TextLabel bold={true}>The editor crashed sorry about that</TextLabel></div>
<div class="widget-span row"><TextLabel>Please report this by filing an issue on GitHub:</TextLabel></div>
<div class="widget-span row"><TextButton label="Report Bug" icon="Warning" noBackground={true} action={() => window.open(githubUrl($dialog.panicDetails), "_blank")} /></div>
<div class="widget-span row"><TextButton label="Report Bug" icon="Warning" flush={true} action={() => window.open(githubUrl($dialog.panicDetails), "_blank")} /></div>
<div class="widget-span row"><TextLabel multiline={true}>Reload the editor to continue. If this occurs<br />immediately on repeated reloads, clear storage:</TextLabel></div>
<div class="widget-span row">
<TextButton
label="Clear Saved Documents"
icon="Trash"
noBackground={true}
flush={true}
action={async () => {
await wipeDocuments();
window.location.reload();
@@ -212,7 +212,7 @@
{/if}
{#each entries as section, sectionIndex (sectionIndex)}
{#if sectionIndex > 0}
<Separator type="List" direction="Vertical" />
<Separator type="Section" direction="Vertical" />
{/if}
{#each virtualScrollingEntryHeight ? section.slice(virtualScrollingStartIndex, virtualScrollingEndIndex) : section as entry, entryIndex (entryIndex + startIndex)}
<LayoutRow
@@ -264,8 +264,12 @@
.floating-menu-container .floating-menu-content.floating-menu-content {
padding: 4px 0;
.separator div {
background: var(--color-4-dimgray);
.separator {
margin: 4px 0;
div {
background: var(--color-4-dimgray);
}
}
.scroll-spacer {
@@ -333,7 +337,7 @@
&:hover,
&.open {
background: var(--color-5-dullgray);
background: var(--color-4-dimgray);
}
&.active {
+13 -13
View File
@@ -450,8 +450,7 @@
<LayoutRow class="spacer" />
{/if}
<LayoutCol class="shelf-bottom-widgets">
<WidgetLayout class={"graph-overlay-button-area"} layout={$document.graphViewOverlayButtonLayout} />
<WidgetLayout class={"working-colors-button-area"} layout={$document.workingColorsLayout} />
<WidgetLayout class={"working-colors-input-area"} layout={$document.workingColorsLayout} />
</LayoutCol>
</LayoutCol>
<LayoutCol class="table">
@@ -519,6 +518,10 @@
.document {
height: 100%;
&.document.document {
padding-bottom: 0;
}
.options-bar {
height: 32px;
flex: 0 0 auto;
@@ -537,12 +540,14 @@
// Enables usage of the `100cqh` unit to reference the height of this container element.
container-type: size;
// Be sure to recalculate this if the items below the tools (working colors and graph overlay buttons) change height in the future.
--height-of-elements-below-tools: 104px;
--height-of-elements-below-tools: 64px;
// Target height for the tools within the container above the lower elements.
--available-height: calc(100cqh - var(--height-of-elements-below-tools));
// Be sure to update this if the height changes as set in `Separator.svelte`.
--separator-height: calc(12px + 1px + 12px);
// The least height required to fit all the tools in 1 column and 2 columns, which the available space must exceed in order for the fewest number of columns to be used.
--1-col-required-height: calc(var(--total-tool-rows-for-1-columns) * 32px + var(--total-separators) * (1px + 8px * 2));
--2-col-required-height: calc(var(--total-tool-rows-for-2-columns) * 32px + var(--total-separators) * (1px + 8px * 2));
--1-col-required-height: calc(var(--total-tool-rows-for-1-columns) * 32px + var(--total-separators) * var(--separator-height));
--2-col-required-height: calc(var(--total-tool-rows-for-2-columns) * 32px + var(--total-separators) * var(--separator-height));
// Evaluates to 0px (if false) or 1px (if true). We multiply by 1000000 to force the result to be an integer 0 or 1 and not interpolate values in-between.
--needs-at-least-2-columns: calc(1px - clamp(0px, calc((var(--available-height) - Min(var(--available-height), var(--1-col-required-height))) * 1000000), 1px));
--needs-at-least-3-columns: calc(1px - clamp(0px, calc((var(--available-height) - Min(var(--available-height), var(--2-col-required-height))) * 1000000), 1px));
@@ -563,7 +568,7 @@
// Remove this when the Firefox bug is fixed.
@-moz-document url-prefix() {
--available-height-plus-1: calc(var(--available-height) + 1px);
--3-col-required-height: calc(var(--total-tool-rows-for-3-columns) * 32px + var(--total-separators) * (1px + 8px * 2));
--3-col-required-height: calc(var(--total-tool-rows-for-3-columns) * 32px + var(--total-separators) * var(--separator-height));
--overflows-with-3-columns: calc(1px - clamp(0px, calc((var(--available-height-plus-1) - Min(var(--available-height-plus-1), var(--3-col-required-height))) * 1000000), 1px));
--firefox-scrollbar-width-space-occupied: 8; // Might change someday, or on different platforms, but this is the value in FF 120 on Windows
padding-right: calc(var(--firefox-scrollbar-width-space-occupied) * var(--overflows-with-3-columns));
@@ -610,17 +615,12 @@
flex: 0 0 auto;
align-items: center;
.graph-overlay-button-area {
height: auto;
align-items: center;
}
.working-colors-button-area {
.working-colors-input-area {
height: auto;
margin: 0;
min-height: 0;
.working-colors-button {
.working-colors-input {
margin: 0;
}
@@ -505,7 +505,6 @@
&::placeholder {
opacity: 1;
color: inherit;
font-style: italic;
}
}
}
@@ -51,6 +51,16 @@
.sections {
flex: 1 1 100%;
// Used as a placeholder for empty assist widgets
.separator.section.horizontal {
margin: 0;
margin-left: 24px;
div {
width: 0;
}
}
}
.text-button {
+6 -47
View File
@@ -741,7 +741,7 @@
</div>
<div class="details">
<!-- TODO: Allow the user to edit the name, just like in the Layers panel -->
<TextLabel tooltip={editor.instance.inDevelopmentMode() ? `Node ID: ${node.id}` : undefined} italic={!node.alias}>{node.alias || "Layer"}</TextLabel>
<TextLabel tooltip={editor.instance.inDevelopmentMode() ? `Node ID: ${node.id}` : undefined}>{node.alias || "Layer"}</TextLabel>
</div>
<svg class="border-mask" width="0" height="0">
@@ -773,14 +773,13 @@
<div class="primary" class:no-parameter-section={exposedInputsOutputs.length === 0}>
<IconLabel icon={nodeIcon(node.name)} />
<!-- TODO: Allow the user to edit the name, just like in the Layers panel -->
<TextLabel tooltip={editor.instance.inDevelopmentMode() ? `Node ID: ${node.id}` : undefined} italic={!node.alias}>{node.alias || node.name}</TextLabel>
<TextLabel tooltip={editor.instance.inDevelopmentMode() ? `Node ID: ${node.id}` : undefined}>{node.alias || node.name}</TextLabel>
</div>
<!-- Parameter rows -->
{#if exposedInputsOutputs.length > 0}
<div class="parameters">
{#each exposedInputsOutputs as parameter, index}
<div class={`parameter expanded ${index < node.exposedInputs.length ? "input" : "output"}`}>
<div class="expand-arrow" />
<TextLabel tooltip={parameter.name}>{parameter.name}</TextLabel>
</div>
{/each}
@@ -1005,10 +1004,6 @@
.icon-label {
fill: var(--color-a-softgray);
}
.expand-arrow::after {
background: var(--icon-expand-collapse-arrow-disabled);
}
}
&.previewed::after {
@@ -1035,34 +1030,6 @@
height: 8px;
}
.expand-arrow {
width: 16px;
height: 16px;
margin: 0;
padding: 0;
position: relative;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
&::after {
content: "";
position: absolute;
width: 8px;
height: 8px;
background: var(--icon-expand-collapse-arrow);
}
&:hover::after {
background: var(--icon-expand-collapse-arrow-hover);
}
}
.expanded .expand-arrow::after {
transform: rotate(90deg);
}
.text-label {
overflow: hidden;
text-overflow: ellipsis;
@@ -1201,8 +1168,8 @@
}
.text-label {
margin-left: 8px; // Remove after reenabling icon-label
margin-right: 4px;
// margin-right: 4px; // Restore after reenabling icon-label
margin: 0 8px;
}
}
@@ -1216,7 +1183,8 @@
position: relative;
display: flex;
align-items: center;
width: 100%;
margin: 0 8px;
width: calc(100% - 8px - 8px);
height: 24px;
&:last-of-type {
@@ -1227,19 +1195,10 @@
width: 100%;
}
&.input {
.expand-arrow {
margin-left: 4px;
}
}
&.output {
flex-direction: row-reverse;
text-align: right;
.expand-arrow {
margin-right: 4px;
}
svg {
width: 30px;
height: 20px;
@@ -44,6 +44,10 @@
flex: 0 0 auto;
margin: 0 4px;
+ .widget-section {
margin-top: 4px;
}
.header {
text-align: left;
align-items: center;
@@ -107,7 +111,6 @@
padding: 0 7px;
padding-top: 1px;
margin-top: -1px;
margin-bottom: 4px;
border: 1px solid var(--color-2-mildblack);
border-radius: 0 0 4px 4px;
overflow: hidden;
@@ -130,8 +133,8 @@
margin-left: 16px;
}
> .parameter-expose-button ~ .text-label:first-of-type {
margin-left: 0;
> .parameter-expose-button + .text-label:first-of-type {
margin-left: 8px;
}
> .text-button {
@@ -12,25 +12,22 @@
import ParameterExposeButton from "@graphite/components/widgets/buttons/ParameterExposeButton.svelte";
import PopoverButton from "@graphite/components/widgets/buttons/PopoverButton.svelte";
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
import WorkingColorsButton from "@graphite/components/widgets/buttons/WorkingColorsButton.svelte";
import CheckboxInput from "@graphite/components/widgets/inputs/CheckboxInput.svelte";
import CurveInput from "@graphite/components/widgets/inputs/CurveInput.svelte";
import DropdownInput from "@graphite/components/widgets/inputs/DropdownInput.svelte";
import FontInput from "@graphite/components/widgets/inputs/FontInput.svelte";
import NumberInput from "@graphite/components/widgets/inputs/NumberInput.svelte";
import OptionalInput from "@graphite/components/widgets/inputs/OptionalInput.svelte";
import PivotInput from "@graphite/components/widgets/inputs/PivotInput.svelte";
import RadioInput from "@graphite/components/widgets/inputs/RadioInput.svelte";
import TextAreaInput from "@graphite/components/widgets/inputs/TextAreaInput.svelte";
import TextInput from "@graphite/components/widgets/inputs/TextInput.svelte";
import WorkingColorsInput from "@graphite/components/widgets/inputs/WorkingColorsInput.svelte";
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
import ImageLabel from "@graphite/components/widgets/labels/ImageLabel.svelte";
import Separator from "@graphite/components/widgets/labels/Separator.svelte";
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
const SUFFIX_WIDGETS = ["PopoverButton"];
const editor = getContext<Editor>("editor");
export let widgetData: WidgetSpanRow | WidgetSpanColumn;
@@ -47,7 +44,6 @@
$: direction = watchDirection(widgetData);
$: widgets = watchWidgets(widgetData);
$: widgetsAndNextSiblingIsSuffix = watchWidgetsAndNextSiblingIsSuffix(widgets);
function watchDirection(widgetData: WidgetSpanRow | WidgetSpanColumn): "row" | "column" | undefined {
if (isWidgetSpanRow(widgetData)) return "row";
@@ -61,17 +57,6 @@
return widgets;
}
function watchWidgetsAndNextSiblingIsSuffix(widgets: Widget[]): [Widget, boolean][] {
return widgets.map((widget, index): [Widget, boolean] => {
// A suffix widget is one that joins up with this widget at the end with only a 1px gap.
// It uses the CSS sibling selector to give its own left edge corners zero radius.
// But this JS is needed to set its preceding sibling widget's right edge corners to zero radius.
const nextSiblingIsSuffix = SUFFIX_WIDGETS.includes(widgets[index + 1]?.props.kind);
return [widget, nextSiblingIsSuffix];
});
}
function updateLayout(index: number, value: unknown) {
editor.instance.updateLayout(layoutTarget, widgets[index].widgetId, value);
}
@@ -88,14 +73,14 @@
<!-- TODO: Refactor this component to use `<svelte:component this={attributesObject} />` to avoid all the separate conditional components -->
<div class={`widget-span ${className} ${extraClasses}`.trim()} class:row={direction === "row"} class:column={direction === "column"}>
{#each widgetsAndNextSiblingIsSuffix as [component, nextIsSuffix], index}
{#each widgets as component, index}
{@const checkboxInput = narrowWidgetProps(component.props, "CheckboxInput")}
{#if checkboxInput}
<CheckboxInput {...exclude(checkboxInput)} on:checked={({ detail }) => updateLayout(index, detail)} />
{/if}
{@const colorInput = narrowWidgetProps(component.props, "ColorButton")}
{#if colorInput}
<ColorButton {...exclude(colorInput)} on:value={({ detail }) => updateLayout(index, detail)} sharpRightCorners={nextIsSuffix} />
<ColorButton {...exclude(colorInput)} on:value={({ detail }) => updateLayout(index, detail)} />
{/if}
{@const curvesInput = narrowWidgetProps(component.props, "CurveInput")}
{#if curvesInput}
@@ -103,11 +88,11 @@
{/if}
{@const dropdownInput = narrowWidgetProps(component.props, "DropdownInput")}
{#if dropdownInput}
<DropdownInput {...exclude(dropdownInput)} on:selectedIndex={({ detail }) => updateLayout(index, detail)} sharpRightCorners={nextIsSuffix} />
<DropdownInput {...exclude(dropdownInput)} on:selectedIndex={({ detail }) => updateLayout(index, detail)} />
{/if}
{@const fontInput = narrowWidgetProps(component.props, "FontInput")}
{#if fontInput}
<FontInput {...exclude(fontInput)} on:changeFont={({ detail }) => updateLayout(index, detail)} sharpRightCorners={nextIsSuffix} />
<FontInput {...exclude(fontInput)} on:changeFont={({ detail }) => updateLayout(index, detail)} />
{/if}
{@const parameterExposeButton = narrowWidgetProps(component.props, "ParameterExposeButton")}
{#if parameterExposeButton}
@@ -115,7 +100,7 @@
{/if}
{@const iconButton = narrowWidgetProps(component.props, "IconButton")}
{#if iconButton}
<IconButton {...exclude(iconButton)} action={() => updateLayout(index, undefined)} sharpRightCorners={nextIsSuffix} />
<IconButton {...exclude(iconButton)} action={() => updateLayout(index, undefined)} />
{/if}
{@const iconLabel = narrowWidgetProps(component.props, "IconLabel")}
{#if iconLabel}
@@ -132,13 +117,8 @@
on:value={({ detail }) => debouncer((value) => updateLayout(index, value)).debounceUpdateValue(detail)}
incrementCallbackIncrease={() => updateLayout(index, "Increment")}
incrementCallbackDecrease={() => updateLayout(index, "Decrement")}
sharpRightCorners={nextIsSuffix}
/>
{/if}
{@const optionalInput = narrowWidgetProps(component.props, "OptionalInput")}
{#if optionalInput}
<OptionalInput {...exclude(optionalInput)} on:checked={({ detail }) => updateLayout(index, detail)} />
{/if}
{@const pivotInput = narrowWidgetProps(component.props, "PivotInput")}
{#if pivotInput}
<PivotInput {...exclude(pivotInput)} on:position={({ detail }) => updateLayout(index, detail)} />
@@ -156,15 +136,15 @@
{/if}
{@const radioInput = narrowWidgetProps(component.props, "RadioInput")}
{#if radioInput}
<RadioInput {...exclude(radioInput)} on:selectedIndex={({ detail }) => updateLayout(index, detail)} sharpRightCorners={nextIsSuffix} />
<RadioInput {...exclude(radioInput)} on:selectedIndex={({ detail }) => updateLayout(index, detail)} />
{/if}
{@const separator = narrowWidgetProps(component.props, "Separator")}
{#if separator}
<Separator {...exclude(separator)} />
{/if}
{@const workingColorsButton = narrowWidgetProps(component.props, "WorkingColorsButton")}
{#if workingColorsButton}
<WorkingColorsButton {...exclude(workingColorsButton)} />
{@const workingColorsInput = narrowWidgetProps(component.props, "WorkingColorsInput")}
{#if workingColorsInput}
<WorkingColorsInput {...exclude(workingColorsInput)} />
{/if}
{@const textAreaInput = narrowWidgetProps(component.props, "TextAreaInput")}
{#if textAreaInput}
@@ -172,7 +152,7 @@
{/if}
{@const textButton = narrowWidgetProps(component.props, "TextButton")}
{#if textButton}
<TextButton {...exclude(textButton)} action={() => updateLayout(index, undefined)} sharpRightCorners={nextIsSuffix} />
<TextButton {...exclude(textButton)} action={() => updateLayout(index, undefined)} />
{/if}
{@const breadcrumbTrailButtons = narrowWidgetProps(component.props, "BreadcrumbTrailButtons")}
{#if breadcrumbTrailButtons}
@@ -180,7 +160,7 @@
{/if}
{@const textInput = narrowWidgetProps(component.props, "TextInput")}
{#if textInput}
<TextInput {...exclude(textInput)} on:commitText={({ detail }) => updateLayout(index, detail)} sharpRightCorners={nextIsSuffix} />
<TextInput {...exclude(textInput)} on:commitText={({ detail }) => updateLayout(index, detail)} />
{/if}
{@const textLabel = narrowWidgetProps(component.props, "TextLabel")}
{#if textLabel}
@@ -218,31 +198,6 @@
--widget-height: 16px;
}
}
// TODO: Target this in a better way than using the tooltip, which will break if changed, or when localized/translated
.checkbox-input [title="Preserve Aspect Ratio"] {
margin-bottom: -32px;
position: relative;
&::before,
&::after {
content: "";
pointer-events: none;
position: absolute;
left: 8px;
width: 1px;
height: 16px;
background: var(--color-7-middlegray);
}
&::before {
top: calc(-4px - 16px);
}
&::after {
bottom: calc(-4px - 16px);
}
}
}
// paddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpadding
</style>
@@ -16,10 +16,9 @@
export let allowNone = false;
// export let allowTransparency = false; // TODO: Implement
export let tooltip: string | undefined = undefined;
export let sharpRightCorners = false;
</script>
<LayoutCol class="color-button" classes={{ disabled, none: value.none, open, "sharp-right-corners": sharpRightCorners }} {tooltip}>
<LayoutCol class="color-button" classes={{ disabled, none: value.none, open }} {tooltip}>
<button {disabled} style:--chosen-color={value.toHexOptionalAlpha()} on:click={() => (open = true)} tabindex="0" data-floating-menu-spawner></button>
{#if disabled && !value.none}
<TextLabel>sRGB</TextLabel>
@@ -59,11 +58,6 @@
}
}
&.sharp-right-corners {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
> button {
border: none;
padding: 0;
@@ -8,7 +8,6 @@
export let disabled = false;
export let active = false;
export let tooltip: string | undefined = undefined;
export let sharpRightCorners = false;
// Callbacks
export let action: (e?: MouseEvent) => void;
@@ -21,17 +20,7 @@
.join(" ");
</script>
<button
class={`icon-button size-${size} ${className} ${extraClasses}`.trim()}
class:disabled
class:active
class:sharp-right-corners={sharpRightCorners}
on:click={action}
{disabled}
title={tooltip}
tabindex={active ? -1 : 0}
{...$$restProps}
>
<button class={`icon-button size-${size} ${className} ${extraClasses}`.trim()} class:disabled class:active on:click={action} {disabled} title={tooltip} tabindex={active ? -1 : 0} {...$$restProps}>
<IconLabel {icon} />
</button>
@@ -9,7 +9,22 @@
</script>
<LayoutRow class="parameter-expose-button">
<button class:exposed style:--data-type-color={`var(--color-data-${dataType})`} on:click={action} title={tooltip} tabindex="-1" />
<button class:exposed style:--data-type-color={`var(--color-data-${dataType})`} on:click={action} title={tooltip} tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<path class="interior" d="M0,7.882c0,1.832,1.325,2.63,2.945,1.772L8.785,6.56c1.62-.858,1.62-2.262,0-3.12L2.945.345C1.325-.512,0,.285,0,2.118Z" />
<path
class="outline"
d="M 1.705180287361145 9.999852180480957 L 1.705180287361145 8.999852180480957 C 1.9275803565979 8.999852180480957 2.194530248641968 8.920772552490234 2.476730346679688
8.771392822265625 L 8.31682014465332 5.67636251449585 C 8.788760185241699 5.426312446594238 9 5.156492233276367 9 5.000002384185791 C 9 4.843512535095215 8.788760185241699
4.573692321777344 8.316730499267578 4.323602199554443 L 2.477190256118774 1.228852391242981 C 2.194520235061646 1.079232335090637 1.927510380744934 1.000152349472046 1.70503032207489
1.000152349472046 C 1.091590285301208 1.000152349472046 1.000000357627869 1.700212359428406 1.000000357627869 2.117512464523315 L 1.000000357627869 7.882492542266846 C
1.000000357627869 8.299762725830078 1.091610312461853 8.999792098999023 1.705130338668823 8.999852180480957 L 1.705180287361145 9.999852180480957 M 1.705027341842651 9.999849319458008
C 0.7003514766693115 9.999751091003418 0 9.214582443237305 0 7.882492542266846 L 0 2.117512464523315 C 0 0.2850223779678345 1.325000405311584 -0.512467622756958 2.945000410079956
0.3450223803520203 L 8.785000801086426 3.440012454986572 C 10.40500068664551 4.298342227935791 10.40500068664551 5.701662540435791 8.785000801086426 6.55999231338501 L
2.945000410079956 9.654982566833496 C 2.502624750137329 9.889138221740723 2.082434415817261 9.999885559082031 1.705027341842651 9.999849319458008 Z"
/>
</svg>
</button>
</LayoutRow>
<style lang="scss" global>
@@ -26,22 +41,38 @@
margin: 0;
padding: 0;
border: none;
border-radius: 50%;
background: none;
fill: none;
stroke: none;
svg {
width: 10px;
height: 10px;
margin-top: -1px;
margin-left: -1px;
}
&:not(.exposed) {
background: none;
border: 1px solid var(--data-type-color);
.outline {
fill: var(--data-type-color);
}
&:hover {
background: var(--color-6-lowergray);
.interior {
fill: var(--color-6-lowergray);
}
}
}
&.exposed {
background: var(--data-type-color);
.interior {
fill: var(--data-type-color);
}
&:hover {
border: 1px solid var(--color-f-white);
.outline {
fill: var(--color-f-white);
}
}
}
}
@@ -1,11 +1,13 @@
<script lang="ts">
import type { IconName } from "@graphite/utility-functions/icons";
import { type IconName, type PopoverButtonStyle } from "@graphite/utility-functions/icons";
import FloatingMenu from "@graphite/components/layout/FloatingMenu.svelte";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte";
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
export let icon: IconName = "DropdownArrow";
export let style: PopoverButtonStyle = "DropdownArrow";
export let icon: IconName | undefined = undefined;
export let tooltip: string | undefined = undefined;
export let disabled = false;
@@ -20,8 +22,11 @@
}
</script>
<LayoutRow class="popover-button">
<IconButton classes={{ open }} {disabled} action={() => onClick()} icon={icon || "DropdownArrow"} size={16} {tooltip} data-floating-menu-spawner />
<LayoutRow class="popover-button" classes={{ "has-icon": icon !== undefined }}>
<IconButton class="dropdown-icon" classes={{ open }} {disabled} action={() => onClick()} icon={style || "DropdownArrow"} size={16} {tooltip} data-floating-menu-spawner />
{#if icon !== undefined}
<IconLabel class="descriptive-icon" classes={{ open }} {disabled} {icon} {tooltip} />
{/if}
<FloatingMenu {open} on:open={({ detail }) => (open = detail)} type="Popover" direction="Bottom">
<slot />
@@ -35,38 +40,40 @@
height: 24px;
flex: 0 0 auto;
.floating-menu {
left: 50%;
bottom: 0;
&.has-icon {
width: 36px;
.dropdown-icon {
padding-left: calc(36px - 16px);
box-sizing: content-box;
}
}
.icon-button.icon-button {
width: 100%;
.dropdown-icon {
width: 16px;
height: 100%;
padding: 0;
border: none;
border-radius: 2px;
background: var(--color-1-nearblack);
fill: var(--color-e-nearwhite);
&:hover,
&.open {
&:hover:not(.disabled),
&.open:not(.disabled) {
background: var(--color-5-dullgray);
}
&.disabled {
background: var(--color-2-mildblack);
fill: var(--color-8-uppergray);
}
}
// TODO: Refactor this and other complicated cases dealing with joined widget margins and border-radius by adding a single standard set of classes: joined-first, joined-inner, and joined-last
div[class*="-input"] + & {
margin-left: 1px;
.descriptive-icon {
width: 16px;
height: 16px;
margin: auto 0;
margin-left: calc(-16px - 16px);
pointer-events: none;
}
.icon-button {
border-radius: 0 2px 2px 0;
}
.floating-menu {
left: 50%;
bottom: 0;
}
}
</style>
@@ -15,11 +15,10 @@
export let label: string;
export let icon: IconName | undefined = undefined;
export let emphasized = false;
export let noBackground = false;
export let flush = false;
export let minWidth = 0;
export let disabled = false;
export let tooltip: string | undefined = undefined;
export let sharpRightCorners = false;
export let menuListChildren: MenuListEntry[][] | undefined = undefined;
// Callbacks
@@ -57,8 +56,7 @@
class:open={self?.open}
class:emphasized
class:disabled
class:no-background={noBackground}
class:sharp-right-corners={sharpRightCorners}
class:flush
style:min-width={minWidth > 0 ? `${minWidth}px` : ""}
title={tooltip}
data-emphasized={emphasized || undefined}
@@ -73,7 +71,7 @@
<IconLabel {icon} />
{/if}
{#if icon && label}
<Separator type={noBackground ? "Unrelated" : "Related"} />
<Separator type={flush ? "Unrelated" : "Related"} />
{/if}
{#if label}
<TextLabel>{label}</TextLabel>
@@ -140,7 +138,7 @@
}
}
&.no-background {
&.flush {
background: none;
&:hover,
@@ -218,7 +218,7 @@
max-width: calc(8 * var(--widget-height));
.grid {
stroke: var(--color-7-middlegray);
stroke: var(--color-5-dullgray);
stroke-width: 0.005;
pointer-events: none;
}
@@ -251,7 +251,7 @@
}
.handle-line {
stroke: var(--color-7-middlegray);
stroke: var(--color-5-dullgray);
stroke-width: 0.005;
pointer-events: none;
}
@@ -21,7 +21,6 @@
export let interactive = true;
export let disabled = false;
export let tooltip: string | undefined = undefined;
export let sharpRightCorners = false;
let activeEntry = makeActiveEntry();
let activeEntrySkipWatcher = false;
@@ -64,7 +63,7 @@
<LayoutRow class="dropdown-input" bind:this={self} data-dropdown-input>
<LayoutRow
class="dropdown-box"
classes={{ disabled, open, "sharp-right-corners": sharpRightCorners }}
classes={{ disabled, open }}
styles={{ "min-width": `${minWidth}px` }}
{tooltip}
on:click={() => !disabled && (open = true)}
@@ -128,7 +127,7 @@
&:hover,
&.open {
background: var(--color-5-dullgray);
background: var(--color-4-dimgray);
}
&.disabled {
@@ -24,7 +24,6 @@
export let disabled = false;
export let textarea = false;
export let tooltip: string | undefined = undefined;
export let sharpRightCorners = false;
export let placeholder: string | undefined = undefined;
export let hideContextMenu = false;
@@ -69,7 +68,7 @@
</script>
<!-- This is a base component, extended by others like NumberInput and TextInput. It should not be used directly. -->
<LayoutRow class={`field-input ${className}`} classes={{ disabled, "sharp-right-corners": sharpRightCorners, ...classes }} style={styleName} {styles} {tooltip}>
<LayoutRow class={`field-input ${className}`} classes={{ disabled, ...classes }} style={styleName} {styles} {tooltip}>
{#if !textarea}
<input
type="text"
@@ -154,14 +153,14 @@
caret-color: var(--color-e-nearwhite);
&::selection {
background-color: var(--color-5-dullgray);
background-color: var(--color-4-dimgray);
// Target only Safari
@supports (background: -webkit-named-image(i)) {
& {
// Setting an alpha value opts out of Safari's "fancy" (but not visible on dark backgrounds) selection highlight rendering
// https://stackoverflow.com/a/71753552/775283
background-color: rgba(var(--color-5-dullgray-rgb), calc(254 / 255));
background-color: rgba(var(--color-4-dimgray-rgb), calc(254 / 255));
}
}
}
@@ -24,7 +24,6 @@
export let isStyle = false;
export let disabled = false;
export let tooltip: string | undefined = undefined;
export let sharpRightCorners = false;
let open = false;
let entries: MenuListEntry[] = [];
@@ -107,7 +106,7 @@
<LayoutRow class="font-input">
<LayoutRow
class="dropdown-box"
classes={{ disabled, "sharp-right-corners": sharpRightCorners }}
classes={{ disabled }}
styles={{ "min-width": `${minWidth}px` }}
{tooltip}
tabindex={disabled ? -1 : 0}
@@ -51,7 +51,6 @@
// Styling
export let minWidth = 0;
export let sharpRightCorners = false;
// Callbacks
export let incrementCallbackIncrease: (() => void) | undefined = undefined;
@@ -581,7 +580,6 @@
{label}
{disabled}
{tooltip}
{sharpRightCorners}
{styles}
hideContextMenu={true}
spellcheck={false}
@@ -658,7 +656,7 @@
background: none;
&:hover {
background: var(--color-5-dullgray);
background: var(--color-4-dimgray);
}
&.right {
@@ -1,36 +0,0 @@
<script lang="ts">
import type { IconName } from "@graphite/utility-functions/icons";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
import CheckboxInput from "@graphite/components/widgets/inputs/CheckboxInput.svelte";
export let checked: boolean;
export let disabled = false;
export let icon: IconName = "Checkmark";
export let tooltip: string | undefined = undefined;
</script>
<LayoutRow class="optional-input" classes={{ disabled }}>
<CheckboxInput {checked} on:checked {disabled} {icon} {tooltip} />
</LayoutRow>
<style lang="scss" global>
.optional-input {
flex-grow: 0;
.checkbox-input label {
align-items: center;
justify-content: center;
white-space: nowrap;
width: 24px;
height: 24px;
border: 1px solid var(--color-5-dullgray);
border-radius: 2px 0 0 2px;
box-sizing: border-box;
}
&.disabled .checkbox-input label {
border: 1px solid var(--color-4-dimgray);
}
}
</style>
@@ -12,7 +12,6 @@
export let entries: RadioEntries;
export let selectedIndex: number | undefined = undefined;
export let disabled = false;
export let sharpRightCorners = false;
function handleEntryClick(radioEntryData: RadioEntryData) {
const index = entries.indexOf(radioEntryData);
@@ -28,7 +27,6 @@
class:active={index === selectedIndex}
class:mixed={selectedIndex === undefined}
class:disabled
class:sharp-right-corners={index === entries.length - 1 && sharpRightCorners}
on:click={() => handleEntryClick(entry)}
title={entry.tooltip}
tabindex={index === selectedIndex ? -1 : 0}
@@ -38,7 +36,7 @@
<IconLabel icon={entry.icon} />
{/if}
{#if entry.label}
<TextLabel>{entry.label}</TextLabel>
<TextLabel italic={selectedIndex === undefined}>{entry.label}</TextLabel>
{/if}
</button>
{/each}
@@ -135,7 +135,7 @@
path {
stroke-width: 1px;
stroke: var(--color-7-middlegray);
stroke: var(--color-6-lowergray);
}
text {
@@ -16,7 +16,6 @@
// Styling
export let centered = false;
export let minWidth = 0;
export let sharpRightCorners = false;
let self: FieldInput | undefined;
let editing = false;
@@ -67,7 +66,6 @@
{disabled}
{tooltip}
{placeholder}
{sharpRightCorners}
bind:this={self}
/>
@@ -6,7 +6,7 @@
</script>
<div class={`separator ${direction.toLowerCase()} ${type.toLowerCase()}`}>
{#if ["Section", "List"].includes(type)}
{#if type === "Section"}
<div />
{/if}
</div>
@@ -21,27 +21,20 @@
}
&.unrelated {
height: 8px;
}
&.section,
&.list {
width: 100%;
div {
height: 1px;
width: calc(100% - 8px);
margin: 0 4px;
background: var(--color-7-middlegray);
}
height: 16px;
}
&.section {
margin: 8px 0;
}
// If changing this, update `--separator-height` in `Document.svelte`
margin: 12px 0;
width: 100%;
&.list {
margin: 4px 0;
div {
margin: 0 4px;
height: 1px;
width: calc(100% - 8px);
background: var(--color-5-dullgray);
}
}
}
@@ -53,27 +46,19 @@
}
&.unrelated {
width: 8px;
}
&.section,
&.list {
height: 100%;
div {
height: calc(100% - 8px);
width: 1px;
margin: 4px 0;
background: var(--color-7-middlegray);
}
width: 16px;
}
&.section {
margin: 0 8px;
}
margin: 0 12px;
height: 100%;
&.list {
margin: 0 4px;
div {
margin: 4px 0;
height: calc(100% - 8px);
width: 1px;
background: var(--color-5-dullgray);
}
}
}
}
@@ -248,11 +248,11 @@
.floating-menu-content .row:hover > & {
.input-key {
border-color: var(--color-7-middlegray);
border-color: var(--color-8-uppergray);
}
.input-mouse .dim {
fill: var(--color-7-middlegray);
fill: var(--color-8-uppergray);
}
}
}
@@ -57,7 +57,8 @@
overflow: hidden;
.separator.section {
margin: 0;
// Width of section separator (12px) minus the margin of the surrounding user input labels (8px)
margin: 0 calc(12px - 8px);
}
.plus,
@@ -77,7 +77,7 @@
<WindowButtonsMac {maximized} />
{:else}
{#each entries as entry}
<TextButton label={entry.label} icon={entry.icon} menuListChildren={entry.children} action={entry.action} noBackground={true} />
<TextButton label={entry.label} icon={entry.icon} menuListChildren={entry.children} action={entry.action} flush={true} />
{/each}
{/if}
</LayoutRow>
@@ -3,7 +3,6 @@
import Layers from "@graphite/components/panels/Layers.svelte";
import Properties from "@graphite/components/panels/Properties.svelte";
import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte";
import PopoverButton from "@graphite/components/widgets/buttons/PopoverButton.svelte";
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
const PANEL_COMPONENTS = {
@@ -102,10 +101,10 @@
</LayoutRow>
{/each}
</LayoutRow>
<PopoverButton icon="VerticalEllipsis">
<!-- <PopoverButton style="VerticalEllipsis">
<TextLabel bold={true}>Panel Options</TextLabel>
<TextLabel multiline={true}>Coming soon</TextLabel>
</PopoverButton>
</PopoverButton> -->
</LayoutRow>
<LayoutCol class="panel-body">
{#if panelType}
@@ -120,7 +119,7 @@
<table>
<tr>
<td>
<TextButton label="New Document" icon="File" noBackground={true} action={() => editor.instance.newDocumentDialog()} />
<TextButton label="New Document" icon="File" flush={true} action={() => editor.instance.newDocumentDialog()} />
</td>
<td>
<UserInputLabel keysWithLabelsGroups={[[...platformModifiers(true), { key: "KeyN", label: "N" }]]} />
@@ -128,7 +127,7 @@
</tr>
<tr>
<td>
<TextButton label="Open Document" icon="Folder" noBackground={true} action={() => editor.instance.openDocument()} />
<TextButton label="Open Document" icon="Folder" flush={true} action={() => editor.instance.openDocument()} />
</td>
<td>
<UserInputLabel keysWithLabelsGroups={[[...platformModifiers(false), { key: "KeyO", label: "O" }]]} />
@@ -136,7 +135,7 @@
</tr>
<tr>
<td colspan="2">
<TextButton label="Open Demo Artwork" icon="Image" noBackground={true} action={() => editor.instance.demoArtworkDialog()} />
<TextButton label="Open Demo Artwork" icon="Image" flush={true} action={() => editor.instance.demoArtworkDialog()} />
</td>
</tr>
</table>
@@ -252,9 +251,9 @@
}
}
.popover-button {
margin: 2px 4px;
}
// .popover-button {
// margin: 2px 4px;
// }
}
.panel-body {
@@ -262,6 +261,10 @@
flex: 1 1 100%;
flex-direction: column;
> div {
padding-bottom: 4px;
}
.empty-panel {
background: var(--color-2-mildblack);
margin: 4px;
-10
View File
@@ -11,7 +11,6 @@ import {
UpdateToolOptionsLayout,
UpdateToolShelfLayout,
UpdateWorkingColorsLayout,
UpdateGraphViewOverlayButtonLayout,
UpdateNodeGraphBarLayout,
TriggerGraphViewOverlay,
} from "@graphite/wasm-communication/messages";
@@ -24,7 +23,6 @@ export function createDocumentState(editor: Editor) {
toolOptionsLayout: defaultWidgetLayout(),
documentBarLayout: defaultWidgetLayout(),
toolShelfLayout: defaultWidgetLayout(),
graphViewOverlayButtonLayout: defaultWidgetLayout(),
workingColorsLayout: defaultWidgetLayout(),
nodeGraphBarLayout: defaultWidgetLayout(),
// Graph view overlay
@@ -69,14 +67,6 @@ export function createDocumentState(editor: Editor) {
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateGraphViewOverlayButtonLayout, async (updateGraphViewOverlayButtonLayout) => {
await tick();
update((state) => {
patchWidgetLayout(state.graphViewOverlayButtonLayout, updateGraphViewOverlayButtonLayout);
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateWorkingColorsLayout, async (updateWorkingColorsLayout) => {
await tick();
+1
View File
@@ -314,6 +314,7 @@ export const ICON_SVG_STRINGS = Object.fromEntries(Object.entries(ICONS).map(([n
export type IconName = keyof typeof ICONS;
export type IconSize = undefined | 12 | 16 | 24 | 32;
export type PopoverButtonStyle = "DropdownArrow" | "VerticalEllipsis";
// The following helper type declarations allow us to avoid manually maintaining the `IconName` type declaration as a string union paralleling the keys of the
// icon definitions. It lets TypeScript do that for us. Our goal is to define the big key-value pair of icons by constraining its values, but inferring its keys.
+8 -21
View File
@@ -3,7 +3,7 @@
import { Transform, Type, plainToClass } from "class-transformer";
import { type IconName, type IconSize } from "@graphite/utility-functions/icons";
import { type PopoverButtonStyle, type IconName, type IconSize } from "@graphite/utility-functions/icons";
import { type WasmEditorInstance, type WasmRawInstance } from "@graphite/wasm-communication/editor";
import type MenuList from "@graphite/components/floating-menus/MenuList.svelte";
@@ -918,18 +918,9 @@ export class NumberInput extends WidgetProps {
minWidth!: number;
}
export class OptionalInput extends WidgetProps {
checked!: boolean;
disabled!: boolean;
icon!: IconName;
@Transform(({ value }: { value: string }) => value || undefined)
tooltip!: string | undefined;
}
export class PopoverButton extends WidgetProps {
style!: PopoverButtonStyle | undefined;
icon!: IconName | undefined;
disabled!: boolean;
@@ -965,7 +956,7 @@ export class RadioInput extends WidgetProps {
}
export type SeparatorDirection = "Horizontal" | "Vertical";
export type SeparatorType = "Related" | "Unrelated" | "Section" | "List";
export type SeparatorType = "Related" | "Unrelated" | "Section";
export class Separator extends WidgetProps {
direction!: SeparatorDirection;
@@ -973,7 +964,7 @@ export class Separator extends WidgetProps {
type!: SeparatorType;
}
export class WorkingColorsButton extends WidgetProps {
export class WorkingColorsInput extends WidgetProps {
@Type(() => Color)
primary!: Color;
@@ -1008,7 +999,7 @@ export class TextButton extends WidgetProps {
emphasized!: boolean;
noBackground!: boolean;
flush!: boolean;
minWidth!: number;
@@ -1029,7 +1020,7 @@ export type TextButtonWidget = {
label: string;
icon?: IconName;
emphasized?: boolean;
noBackground?: boolean;
flush?: boolean;
minWidth?: number;
disabled?: boolean;
tooltip?: string;
@@ -1103,13 +1094,12 @@ const widgetSubTypes = [
{ value: IconLabel, name: "IconLabel" },
{ value: ImageLabel, name: "ImageLabel" },
{ value: NumberInput, name: "NumberInput" },
{ value: OptionalInput, name: "OptionalInput" },
{ value: ParameterExposeButton, name: "ParameterExposeButton" },
{ value: PivotInput, name: "PivotInput" },
{ value: PopoverButton, name: "PopoverButton" },
{ value: RadioInput, name: "RadioInput" },
{ value: Separator, name: "Separator" },
{ value: WorkingColorsButton, name: "WorkingColorsButton" },
{ value: WorkingColorsInput, name: "WorkingColorsInput" },
{ value: TextAreaInput, name: "TextAreaInput" },
{ value: TextButton, name: "TextButton" },
{ value: TextInput, name: "TextInput" },
@@ -1293,8 +1283,6 @@ export class UpdateDocumentBarLayout extends WidgetDiffUpdate {}
export class UpdateDocumentModeLayout extends WidgetDiffUpdate {}
export class UpdateGraphViewOverlayButtonLayout extends WidgetDiffUpdate {}
export class UpdateLayersPanelOptionsLayout extends WidgetDiffUpdate {}
// Extends JsMessage instead of WidgetDiffUpdate because the menu bar isn't diffed
@@ -1384,7 +1372,6 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateDocumentRulers,
UpdateDocumentScrollbars,
UpdateEyedropperSamplingState,
UpdateGraphViewOverlayButtonLayout,
UpdateImageData,
UpdateInputHints,
UpdateLayersPanelOptionsLayout,