mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 23:08:11 +08:00
Make the tool shelf adapt to multiple columns and improve panel scrollbars
Closes #176
This commit is contained in:
@@ -281,7 +281,7 @@ impl LayoutHolder for ToolData {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Layout::WidgetLayout(WidgetLayout {
|
Layout::WidgetLayout(WidgetLayout {
|
||||||
layout: vec![LayoutGroup::Column { widgets: tool_groups_layout }],
|
layout: vec![LayoutGroup::Row { widgets: tool_groups_layout }],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,25 +198,18 @@
|
|||||||
.layout-col {
|
.layout-col {
|
||||||
.scrollable-x,
|
.scrollable-x,
|
||||||
.scrollable-y {
|
.scrollable-y {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
// Firefox (standardized in CSS, but less capable)
|
// Firefox (standardized in CSS, but less capable)
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: var(--color-5-dullgray) transparent;
|
scrollbar-color: var(--color-5-dullgray) transparent;
|
||||||
|
|
||||||
&:not(:hover) {
|
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebKit (only in Chromium/Safari but more capable)
|
// WebKit (only in Chromium/Safari but more capable)
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: calc(2px + 6px + 2px);
|
width: calc(2px + 6px + 2px);
|
||||||
height: calc(2px + 6px + 2px);
|
height: calc(2px + 6px + 2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:hover)::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
box-shadow: inset 0 0 0 1px var(--color-5-dullgray);
|
box-shadow: inset 0 0 0 1px var(--color-5-dullgray);
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
$: displayTail = open && type === "Popover";
|
$: displayTail = open && type === "Popover";
|
||||||
$: displayContainer = open || measuringOngoing;
|
$: displayContainer = open || measuringOngoing;
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
.join(" ");
|
.join(" ");
|
||||||
$: extraStyles = Object.entries(styles)
|
$: extraStyles = Object.entries(styles)
|
||||||
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
let self: HTMLDivElement | undefined;
|
let self: HTMLDivElement | undefined;
|
||||||
|
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
.join(" ");
|
.join(" ");
|
||||||
$: extraStyles = Object.entries(styles)
|
$: extraStyles = Object.entries(styles)
|
||||||
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
let self: HTMLDivElement | undefined;
|
let self: HTMLDivElement | undefined;
|
||||||
|
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
.join(" ");
|
.join(" ");
|
||||||
$: extraStyles = Object.entries(styles)
|
$: extraStyles = Object.entries(styles)
|
||||||
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
UpdateEyedropperSamplingState,
|
UpdateEyedropperSamplingState,
|
||||||
UpdateMouseCursor,
|
UpdateMouseCursor,
|
||||||
UpdateDocumentNodeRender,
|
UpdateDocumentNodeRender,
|
||||||
|
isWidgetSpanRow,
|
||||||
} from "@graphite/wasm-communication/messages";
|
} from "@graphite/wasm-communication/messages";
|
||||||
|
|
||||||
import EyedropperPreview, { ZOOM_WINDOW_DIMENSIONS } from "@graphite/components/floating-menus/EyedropperPreview.svelte";
|
import EyedropperPreview, { ZOOM_WINDOW_DIMENSIONS } from "@graphite/components/floating-menus/EyedropperPreview.svelte";
|
||||||
@@ -80,6 +81,39 @@
|
|||||||
|
|
||||||
$: canvasWidthCSS = canvasDimensionCSS(canvasSvgWidth);
|
$: canvasWidthCSS = canvasDimensionCSS(canvasSvgWidth);
|
||||||
$: canvasHeightCSS = canvasDimensionCSS(canvasSvgHeight);
|
$: canvasHeightCSS = canvasDimensionCSS(canvasSvgHeight);
|
||||||
|
$: toolShelfTotalToolsAndSeparators = ((layoutGroup) => {
|
||||||
|
if (!isWidgetSpanRow(layoutGroup)) return undefined;
|
||||||
|
|
||||||
|
let totalSeparators = 0;
|
||||||
|
let totalToolRowsFor1Columns = 0;
|
||||||
|
let totalToolRowsFor2Columns = 0;
|
||||||
|
let totalToolRowsFor3Columns = 0;
|
||||||
|
|
||||||
|
const tally = () => {
|
||||||
|
totalToolRowsFor1Columns += toolsInCurrentGroup;
|
||||||
|
totalToolRowsFor2Columns += Math.ceil(toolsInCurrentGroup / 2);
|
||||||
|
totalToolRowsFor3Columns += Math.ceil(toolsInCurrentGroup / 3);
|
||||||
|
toolsInCurrentGroup = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
let toolsInCurrentGroup = 0;
|
||||||
|
layoutGroup.rowWidgets.forEach((widget) => {
|
||||||
|
if (widget.props.kind === "Separator") {
|
||||||
|
totalSeparators += 1;
|
||||||
|
tally();
|
||||||
|
} else {
|
||||||
|
toolsInCurrentGroup += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tally();
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalSeparators,
|
||||||
|
totalToolRowsFor1Columns,
|
||||||
|
totalToolRowsFor2Columns,
|
||||||
|
totalToolRowsFor3Columns,
|
||||||
|
};
|
||||||
|
})($document.toolShelfLayout.layout[0]);
|
||||||
|
|
||||||
function pasteFile(e: DragEvent) {
|
function pasteFile(e: DragEvent) {
|
||||||
const { dataTransfer } = e;
|
const { dataTransfer } = e;
|
||||||
@@ -428,17 +462,26 @@
|
|||||||
<WidgetLayout layout={$document.nodeGraphBarLayout} />
|
<WidgetLayout layout={$document.nodeGraphBarLayout} />
|
||||||
{/if}
|
{/if}
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="shelf-and-table">
|
<LayoutRow
|
||||||
|
class="shelf-and-table"
|
||||||
|
styles={toolShelfTotalToolsAndSeparators && {
|
||||||
|
"--total-separators": toolShelfTotalToolsAndSeparators.totalSeparators,
|
||||||
|
"--total-tool-rows-for-1-columns": toolShelfTotalToolsAndSeparators.totalToolRowsFor1Columns,
|
||||||
|
"--total-tool-rows-for-2-columns": toolShelfTotalToolsAndSeparators.totalToolRowsFor2Columns,
|
||||||
|
"--total-tool-rows-for-3-columns": toolShelfTotalToolsAndSeparators.totalToolRowsFor3Columns,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<LayoutCol class="shelf">
|
<LayoutCol class="shelf">
|
||||||
{#if !$document.graphViewOverlayOpen}
|
{#if !$document.graphViewOverlayOpen}
|
||||||
<LayoutCol class="tools" scrollableY={true}>
|
<LayoutCol class="tools" scrollableY={true}>
|
||||||
<WidgetLayout layout={$document.toolShelfLayout} />
|
<WidgetLayout layout={$document.toolShelfLayout} />
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
|
{:else}
|
||||||
|
<LayoutRow class="spacer" />
|
||||||
{/if}
|
{/if}
|
||||||
<LayoutCol class="spacer" />
|
|
||||||
<LayoutCol class="shelf-bottom-widgets">
|
<LayoutCol class="shelf-bottom-widgets">
|
||||||
<WidgetLayout layout={$document.graphViewOverlayButtonLayout} />
|
<WidgetLayout class={"graph-overlay-button-area"} layout={$document.graphViewOverlayButtonLayout} />
|
||||||
<WidgetLayout layout={$document.workingColorsLayout} />
|
<WidgetLayout class={"working-colors-button-area"} layout={$document.workingColorsLayout} />
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
<LayoutCol class="table">
|
<LayoutCol class="table">
|
||||||
@@ -523,71 +566,102 @@
|
|||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.for-graph .widget-layout {
|
&.for-graph {
|
||||||
flex-direction: row;
|
|
||||||
flex-grow: 1;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.shelf-and-table {
|
.shelf-and-table {
|
||||||
|
// 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;
|
||||||
|
// Target height for the tools within the container above the lower elements.
|
||||||
|
--available-height: calc(100cqh - var(--height-of-elements-below-tools));
|
||||||
|
// 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));
|
||||||
|
// 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));
|
||||||
|
--columns: calc(1px + var(--needs-at-least-2-columns) + var(--needs-at-least-3-columns));
|
||||||
|
|
||||||
.shelf {
|
.shelf {
|
||||||
width: 32px;
|
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
.tools {
|
.tools {
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
|
|
||||||
.icon-button[title^="Coming Soon"] {
|
// Firefox-specific workaround for this bug causing the scrollbar to cover up the toolbar instead of widening to accommodate the scrollbar:
|
||||||
opacity: 0.25;
|
// <https://bugzilla.mozilla.org/show_bug.cgi?id=764076>
|
||||||
transition: opacity 0.25s;
|
// <https://stackoverflow.com/questions/63278303/firefox-does-not-take-vertical-scrollbar-width-into-account-when-calculating-par>
|
||||||
|
// Remove this when the Firefox bug is fixed.
|
||||||
&:hover {
|
@-moz-document url-prefix() {
|
||||||
opacity: 1;
|
--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));
|
||||||
|
--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));
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-button:not(.active) {
|
.widget-span {
|
||||||
.color-general {
|
flex-wrap: wrap;
|
||||||
fill: var(--color-data-general);
|
width: calc(var(--columns) * 32);
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&[title^="Coming Soon"] {
|
||||||
|
opacity: 0.25;
|
||||||
|
transition: opacity 0.25s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.active) {
|
||||||
|
.color-general {
|
||||||
|
fill: var(--color-data-general);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-vector {
|
||||||
|
fill: var(--color-data-vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-raster {
|
||||||
|
fill: var(--color-data-raster);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-vector {
|
.separator {
|
||||||
fill: var(--color-data-vector);
|
min-height: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.color-raster {
|
|
||||||
fill: var(--color-data-raster);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.spacer {
|
|
||||||
flex: 1 0 auto;
|
|
||||||
min-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shelf-bottom-widgets {
|
.shelf-bottom-widgets {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.widget-layout:first-of-type {
|
.graph-overlay-button-area {
|
||||||
height: auto;
|
height: auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.widget-layout:last-of-type {
|
.working-colors-button-area {
|
||||||
height: auto;
|
height: auto;
|
||||||
|
margin: 0;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
.widget-span.row {
|
.working-colors-button {
|
||||||
min-height: 0;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.working-colors-button {
|
.icon-button {
|
||||||
margin: 0;
|
--widget-height: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
--widget-height: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -363,7 +363,6 @@
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.widget-span {
|
.widget-span {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -30,23 +30,23 @@
|
|||||||
<LayoutRow class="options-bar">
|
<LayoutRow class="options-bar">
|
||||||
<WidgetLayout layout={propertiesOptionsLayout} />
|
<WidgetLayout layout={propertiesOptionsLayout} />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="sections" scrollableY={true}>
|
<LayoutCol class="sections" scrollableY={true}>
|
||||||
<WidgetLayout layout={propertiesSectionsLayout} />
|
<WidgetLayout layout={propertiesSectionsLayout} />
|
||||||
</LayoutRow>
|
</LayoutCol>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
|
|
||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
.properties {
|
.properties {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
flex: 1 1 100%;
|
||||||
.widget-layout {
|
|
||||||
flex: 1 1 100%;
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.options-bar {
|
.options-bar {
|
||||||
height: 32px;
|
min-height: 32px;
|
||||||
flex: 0 0 auto;
|
margin: 0 4px;
|
||||||
|
|
||||||
|
.widget-span {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sections {
|
.sections {
|
||||||
|
|||||||
@@ -8,29 +8,16 @@
|
|||||||
let className = "";
|
let className = "";
|
||||||
export { className as class };
|
export { className as class };
|
||||||
export let classes: Record<string, boolean> = {};
|
export let classes: Record<string, boolean> = {};
|
||||||
|
|
||||||
$: extraClasses = Object.entries(classes)
|
|
||||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
|
||||||
.join(" ");
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={`widget-layout ${className} ${extraClasses}`.trim()}>
|
{#each layout.layout as layoutGroup}
|
||||||
{#each layout.layout as layoutGroup}
|
{#if isWidgetSpanRow(layoutGroup) || isWidgetSpanColumn(layoutGroup)}
|
||||||
{#if isWidgetSpanRow(layoutGroup) || isWidgetSpanColumn(layoutGroup)}
|
<WidgetSpan widgetData={layoutGroup} layoutTarget={layout.layoutTarget} class={className} {classes} />
|
||||||
<WidgetSpan widgetData={layoutGroup} layoutTarget={layout.layoutTarget} />
|
{:else if isWidgetSection(layoutGroup)}
|
||||||
{:else if isWidgetSection(layoutGroup)}
|
<WidgetSection widgetData={layoutGroup} layoutTarget={layout.layoutTarget} class={className} {classes} />
|
||||||
<WidgetSection widgetData={layoutGroup} layoutTarget={layout.layoutTarget} />
|
{:else}
|
||||||
{:else}
|
<span style="color: #d6536e">Error: The widget layout that belongs here has an invalid layout group type</span>
|
||||||
<span style="color: #d6536e">Error: The widget layout that belongs here has an invalid layout group type</span>
|
{/if}
|
||||||
{/if}
|
{/each}
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="scss" global>
|
<style lang="scss" global></style>
|
||||||
.widget-layout {
|
|
||||||
height: 100%;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -9,11 +9,15 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export let layoutTarget: any; // TODO: Give type
|
export let layoutTarget: any; // TODO: Give type
|
||||||
|
|
||||||
|
let className = "";
|
||||||
|
export { className as class };
|
||||||
|
export let classes: Record<string, boolean> = {};
|
||||||
|
|
||||||
let expanded = true;
|
let expanded = true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- TODO: Implement collapsable sections with properties system -->
|
<!-- TODO: Implement collapsable sections with properties system -->
|
||||||
<LayoutCol class="widget-section">
|
<LayoutCol class={`widget-section ${className}`.trim()} {classes}>
|
||||||
<button class="header" class:expanded on:click|stopPropagation={() => (expanded = !expanded)} tabindex="0">
|
<button class="header" class:expanded on:click|stopPropagation={() => (expanded = !expanded)} tabindex="0">
|
||||||
<div class="expand-arrow" />
|
<div class="expand-arrow" />
|
||||||
<TextLabel bold={true}>{widgetData.name}</TextLabel>
|
<TextLabel bold={true}>{widgetData.name}</TextLabel>
|
||||||
@@ -38,6 +42,7 @@
|
|||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
.widget-section {
|
.widget-section {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
margin: 0 4px;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|||||||
@@ -38,6 +38,14 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export let layoutTarget: any;
|
export let layoutTarget: any;
|
||||||
|
|
||||||
|
let className = "";
|
||||||
|
export { className as class };
|
||||||
|
export let classes: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
$: extraClasses = Object.entries(classes)
|
||||||
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
$: direction = watchDirection(widgetData);
|
$: direction = watchDirection(widgetData);
|
||||||
$: widgets = watchWidgets(widgetData);
|
$: widgets = watchWidgets(widgetData);
|
||||||
$: widgetsAndNextSiblingIsSuffix = watchWidgetsAndNextSiblingIsSuffix(widgets);
|
$: widgetsAndNextSiblingIsSuffix = watchWidgetsAndNextSiblingIsSuffix(widgets);
|
||||||
@@ -81,7 +89,7 @@
|
|||||||
<!-- TODO: Refactor this component to use `<svelte:component this={attributesObject} />` to avoid all the separate conditional components -->
|
<!-- TODO: Refactor this component to use `<svelte:component this={attributesObject} />` to avoid all the separate conditional components -->
|
||||||
<!-- TODO: Also rename this component, and probably move the `widget-${direction}` wrapper to be part of `WidgetLayout.svelte` as part of its refactor -->
|
<!-- TODO: Also rename this component, and probably move the `widget-${direction}` wrapper to be part of `WidgetLayout.svelte` as part of its refactor -->
|
||||||
|
|
||||||
<div class="widget-span" class:row={direction === "row"} class:column={direction === "column"}>
|
<div class={`widget-span ${className} ${extraClasses}`.trim()} class:row={direction === "row"} class:column={direction === "column"}>
|
||||||
{#each widgetsAndNextSiblingIsSuffix as [component, nextIsSuffix], index}
|
{#each widgetsAndNextSiblingIsSuffix as [component, nextIsSuffix], index}
|
||||||
{@const checkboxInput = narrowWidgetProps(component.props, "CheckboxInput")}
|
{@const checkboxInput = narrowWidgetProps(component.props, "CheckboxInput")}
|
||||||
{#if checkboxInput}
|
{#if checkboxInput}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
export let classes: Record<string, boolean> = {};
|
export let classes: Record<string, boolean> = {};
|
||||||
|
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
.join(" ");
|
.join(" ");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
return `size-${ICONS[icon].size}`;
|
return `size-${ICONS[icon].size}`;
|
||||||
})(icon);
|
})(icon);
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
.join(" ");
|
.join(" ");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
export let tooltip: string | undefined = undefined;
|
export let tooltip: string | undefined = undefined;
|
||||||
|
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
.join(" ");
|
.join(" ");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
export let tooltip: string | undefined = undefined;
|
export let tooltip: string | undefined = undefined;
|
||||||
|
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
.join(" ");
|
.join(" ");
|
||||||
$: extraStyles = Object.entries(styles)
|
$: extraStyles = Object.entries(styles)
|
||||||
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
||||||
|
|||||||
@@ -178,7 +178,7 @@
|
|||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
height: 100%;
|
height: 28px;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -216,7 +216,7 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
// Height and line-height required because https://stackoverflow.com/a/21611191/775283
|
// Height and line-height required because https://stackoverflow.com/a/21611191/775283
|
||||||
height: 100%;
|
height: 28px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1271,17 +1271,17 @@ export type LayoutGroup = WidgetSpanRow | WidgetSpanColumn | WidgetSection;
|
|||||||
|
|
||||||
export type WidgetSpanColumn = { columnWidgets: Widget[] };
|
export type WidgetSpanColumn = { columnWidgets: Widget[] };
|
||||||
export function isWidgetSpanColumn(layoutColumn: LayoutGroup): layoutColumn is WidgetSpanColumn {
|
export function isWidgetSpanColumn(layoutColumn: LayoutGroup): layoutColumn is WidgetSpanColumn {
|
||||||
return Boolean((layoutColumn as WidgetSpanColumn).columnWidgets);
|
return Boolean((layoutColumn as WidgetSpanColumn)?.columnWidgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WidgetSpanRow = { rowWidgets: Widget[] };
|
export type WidgetSpanRow = { rowWidgets: Widget[] };
|
||||||
export function isWidgetSpanRow(layoutRow: LayoutGroup): layoutRow is WidgetSpanRow {
|
export function isWidgetSpanRow(layoutRow: LayoutGroup): layoutRow is WidgetSpanRow {
|
||||||
return Boolean((layoutRow as WidgetSpanRow).rowWidgets);
|
return Boolean((layoutRow as WidgetSpanRow)?.rowWidgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WidgetSection = { name: string; layout: LayoutGroup[] };
|
export type WidgetSection = { name: string; layout: LayoutGroup[] };
|
||||||
export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSection {
|
export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSection {
|
||||||
return Boolean((layoutRow as WidgetSection).layout);
|
return Boolean((layoutRow as WidgetSection)?.layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpacking rust types to more usable type in the frontend
|
// Unpacking rust types to more usable type in the frontend
|
||||||
|
|||||||
Reference in New Issue
Block a user