From e470c715e53c035b01947cb0b1f45b167220a928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20C=C3=A1mera?= <81186472+ncamera@users.noreply.github.com> Date: Tue, 3 Dec 2024 00:21:45 -0300 Subject: [PATCH] `[ch-tab-render][ch-flexible-layout-render]` Remove the `direction` property in favor of the `tabListPosition` property (#460) * Remove the "direction" property in favor of the "tabListPosition" property The new "tabListPosition" property allows users have more control over the position of the ch-tab-render's tab-list. Also, refactor the styling with parts to improve semantics and make the ch-tab-render more customizable. Breaking changes: - Removed the "direction" property in favor of the "tabListPosition" property. Migrating from "direction" to "tabListPosition": + direction = "block" ---> tabListPosition = "block-start" + direction = "inline" ---> tabListPosition = "inline-start" - The following parts were renamed: + "button" ---> "tab" + "page" ---> "panel" + "page-container" ---> "panel-container" - Removed the "drag-preview" and "drag-preview-element" parts in favor of the "tab dragging" part. - "Removed the "drag-preview--outside-tab-list" part in favor of the "dragging-out-of-tab-list" part. - "Removed the "drag-preview--inside-tab-list__block" part in favor of the "dragging-over-tab-list block" part. - "Removed the "drag-preview--inside-tab-list__inline" part in favor of the "dragging-over-tab-list inline" part. - Removed "block-size: 100%" CSS property from the ch-tab-render, since it can break margin styling. Use "display: grid" in the ch-tab-render container or a different solution to stretch the ch-tab-render to the parent container size. * Removed tabDirection and tabPosition properties in favor of the tabListPosition property Same breaking changes applies for the ch-flexible-layout-render. Simplified part exporting to avoid exporting parts that are not used. * Use reserved-names in layout-splitter * Fix breaking changes in showcase * Improve styling with parts in the flexible-layout component * Improve showcase * Fix unit tests --- src/common/reserved-names.ts | 62 +++- .../flexible-layout-render.tsx | 2 +- .../flexible-layout/flexible-layout.scss | 14 - .../flexible-layout/flexible-layout.tsx | 69 ++-- .../internal/flexible-layout/types.ts | 16 +- .../flexible-layout/tests/slots.e2e.ts | 2 +- src/components/flexible-layout/utils.ts | 26 +- .../layout-splitter/layout-splitter.tsx | 8 +- src/components/tab/tab.scss | 172 +++++---- src/components/tab/tab.tsx | 345 ++++++++++-------- src/components/tab/types.ts | 6 +- src/components/tab/utils.ts | 83 +---- .../test/test-flexible-layout/renders.tsx | 61 +--- .../test-action-group-styles.scss | 9 +- .../test-flexible-layout.scss | 104 +----- .../test-flexible-layout.tsx | 18 +- .../accordion/accordion.showcase.tsx | 11 +- .../barcode-scanner.showcase.tsx | 12 +- .../components/checkbox/checkbox.showcase.tsx | 10 +- .../combo-box/combo-box.showcase.tsx | 20 +- .../components/dialog/dialog.showcase.tsx | 24 +- .../assets/components/edit/edit.showcase.tsx | 11 +- .../components/markdown/markdown.showcase.tsx | 15 +- .../components/popover/popover.showcase.tsx | 19 +- .../radio-group/radio-group.showcase.tsx | 10 +- src/showcase/assets/components/renders.ts | 1 - .../components/switch/switch.showcase.tsx | 10 +- .../assets/components/tab/tab.showcase.tsx | 27 +- src/showcase/assets/components/utils.ts | 8 + 29 files changed, 539 insertions(+), 636 deletions(-) diff --git a/src/common/reserved-names.ts b/src/common/reserved-names.ts index d6a3a1456..be4abf25b 100644 --- a/src/common/reserved-names.ts +++ b/src/common/reserved-names.ts @@ -1,7 +1,7 @@ import { ImageRender } from "./types"; const joinParts = (parts: { [key in string]: string }) => - [...Object.values(parts)].join(","); + Object.values(parts).join(","); /** * Useful key codes that must be used in KeyboardEvent.code @@ -282,6 +282,33 @@ export const DROPDOWN_PARTS_DICTIONARY = { export const DROPDOWN_EXPORT_PARTS = joinParts(DROPDOWN_PARTS_DICTIONARY); +// - - - - - - - - - - - - - - - - - - - - +// Flexible Layout Parts +// - - - - - - - - - - - - - - - - - - - - +export const FLEXIBLE_LAYOUT_PARTS_DICTIONARY = { + DROPPABLE_AREA: "droppable-area", + LEAF: "leaf" + + // - - - - - - - - States - - - - - - - - +} as const; + +export const FLEXIBLE_LAYOUT_EXPORT_PARTS = joinParts( + FLEXIBLE_LAYOUT_PARTS_DICTIONARY +); + +// - - - - - - - - - - - - - - - - - - - - +// Layout Splitter Parts +// - - - - - - - - - - - - - - - - - - - - +export const LAYOUT_SPLITTER_PARTS_DICTIONARY = { + BAR: "bar" + + // - - - - - - - - States - - - - - - - - +} as const; + +export const LAYOUT_SPLITTER_EXPORT_PARTS = joinParts( + LAYOUT_SPLITTER_PARTS_DICTIONARY +); + // - - - - - - - - - - - - - - - - - - - - // Navigation List Parts // - - - - - - - - - - - - - - - - - - - - @@ -372,7 +399,38 @@ export const SWITCH_PARTS_DICTIONARY = { export const SWITCH_EXPORT_PARTS = joinParts(SWITCH_PARTS_DICTIONARY); // - - - - - - - - - - - - - - - - - - - - -// Tabular Grid view Parts +// Tab Parts +// - - - - - - - - - - - - - - - - - - - - +export const TAB_PARTS_DICTIONARY = { + TAB: "tab", + CLOSE_BUTTON: "close-button", + LIST: "tab-list", + PANEL: "tab-panel", + PANEL_CONTAINER: "tab-panel-container", + IMAGE: "img", + + // - - - - - - - - States - - - - - - - - + CLOSABLE: "closable", // TAB + NOT_CLOSABLE: "not-closable", // TAB + DISABLED: "disabled", // TAB, PANEL, CLOSE_BUTTON + DRAGGING: "dragging", // TAB, CLOSE_BUTTON, LIST + DRAGGING_OVER_TAB_LIST: "dragging-over-tab-list", // TAB, CLOSE_BUTTON, LIST + DRAGGING_OUT_OF_TAB_LIST: "dragging-out-of-tab-list", // TAB, CLOSE_BUTTON, LIST + EXPANDED: "expanded", // PANEL_CONTAINER + COLLAPSED: "collapsed", // PANEL_CONTAINER + SELECTED: "selected", // TAB, PANEL, CLOSE_BUTTON + NOT_SELECTED: "not-selected", // TAB, PANEL, CLOSE_BUTTON + + BLOCK: "block", // TAB, CLOSE_BUTTON, TAB_LIST, PANEL, PANEL_CONTAINER + INLINE: "inline", // TAB, CLOSE_BUTTON, TAB_LIST, PANEL, PANEL_CONTAINER + START: "start", // TAB, CLOSE_BUTTON, TAB_LIST, PANEL, PANEL_CONTAINER + END: "end" // TAB, CLOSE_BUTTON, TAB_LIST, PANEL, PANEL_CONTAINER +} as const; + +export const TAB_EXPORT_PARTS = joinParts(TAB_PARTS_DICTIONARY); + +// - - - - - - - - - - - - - - - - - - - - +// Tabular Grid view Parts // - - - - - - - - - - - - - - - - - - - - export const TABULAR_GRID_PARTS_DICTIONARY = { COLUMNSET: "columnset", diff --git a/src/components/flexible-layout/flexible-layout-render.tsx b/src/components/flexible-layout/flexible-layout-render.tsx index fe3ca2431..8cb53c79c 100644 --- a/src/components/flexible-layout/flexible-layout-render.tsx +++ b/src/components/flexible-layout/flexible-layout-render.tsx @@ -758,7 +758,7 @@ export class ChFlexibleLayoutRender { id: newLeafToAddId, selectedWidgetId: widget.id, size: undefined, - tabDirection: viewTargetUIModel.leafInfo.tabDirection, + tabListPosition: viewTargetUIModel.leafInfo.tabListPosition, type: "tabbed", widgets: [widget], dragBar: { diff --git a/src/components/flexible-layout/internal/flexible-layout/flexible-layout.scss b/src/components/flexible-layout/internal/flexible-layout/flexible-layout.scss index a95bc1563..0905f6cf4 100644 --- a/src/components/flexible-layout/internal/flexible-layout/flexible-layout.scss +++ b/src/components/flexible-layout/internal/flexible-layout/flexible-layout.scss @@ -22,17 +22,3 @@ transition: inset 150ms ease-in-out; pointer-events: none; // Avoid the capture of pointer events } - -.ch-tab-inline--end { - grid-template-columns: 1fr max-content; -} - -.ch-tab-block--end { - grid-template-rows: 1fr max-content; -} - -// Change the visual order, but no the order in the HTML -.ch-tab-inline--end::part(tab-list), -.ch-tab-block--end::part(tab-list) { - order: 1; -} diff --git a/src/components/flexible-layout/internal/flexible-layout/flexible-layout.tsx b/src/components/flexible-layout/internal/flexible-layout/flexible-layout.tsx index 379e25734..27dda7c72 100644 --- a/src/components/flexible-layout/internal/flexible-layout/flexible-layout.tsx +++ b/src/components/flexible-layout/internal/flexible-layout/flexible-layout.tsx @@ -37,11 +37,22 @@ import { removeDroppableAreaStyles } from "./utils"; import { getLeafInfo } from "../../utils"; -import { isRTL } from "../../../../common/utils"; +import { isRTL, tokenMap } from "../../../../common/utils"; import { CssContainProperty, CssOverflowProperty } from "../../../../common/types"; +import { + DEFAULT_TAB_LIST_POSITION, + isBlockDirection, + isStartDirection +} from "../../../tab/utils"; +import { + FLEXIBLE_LAYOUT_PARTS_DICTIONARY, + LAYOUT_SPLITTER_PARTS_DICTIONARY, + TAB_EXPORT_PARTS, + TAB_PARTS_DICTIONARY +} from "../../../../common/reserved-names"; const LEAF_SELECTOR = (id: string) => `[id="${id}"]`; @@ -464,33 +475,41 @@ export class ChFlexibleLayout { }; #computePartsToExport = () => { - const exportPartsSet = new Set([ - "bar", - "block", - "inline", - "leaf", - "start", - "end" + const exportPartsSet = new Set([ + ...Object.values(TAB_PARTS_DICTIONARY) ]); + exportPartsSet.add(FLEXIBLE_LAYOUT_PARTS_DICTIONARY.DROPPABLE_AREA); + exportPartsSet.add(FLEXIBLE_LAYOUT_PARTS_DICTIONARY.LEAF); + exportPartsSet.add(LAYOUT_SPLITTER_PARTS_DICTIONARY.BAR); + // TODO: Test items that have a part with spaces this.layoutSplitterParts.forEach(part => exportPartsSet.add(part)); // TODO: Revisit this algorithm to simplify definition of exportparts this.#leafs.forEach(leaf => { if (leaf.type === "tabbed") { exportPartsSet.add(leaf.id); - exportPartsSet.add(leaf.exportParts); + exportPartsSet.add(leaf.tabListPosition ?? DEFAULT_TAB_LIST_POSITION); + leaf.widgets.forEach(({ id }) => exportPartsSet.add(id)); } }); this.#exportParts = [...exportPartsSet.keys()].join(","); - this.#layoutSplitterExportParts = - [...exportPartsSet.keys()].join(",") + ",bar"; + + this.#layoutSplitterExportParts = [ + ...this.layoutSplitterParts.keys(), + LAYOUT_SPLITTER_PARTS_DICTIONARY.BAR + ].join(","); }; #renderTab = (viewInfo: FlexibleLayoutLeafInfo<"tabbed">) => { const dragOutsideEnabled = viewInfo.dragOutside ?? this.dragOutside; const sortableEnabled = viewInfo.sortable ?? this.sortable; + const tabListPosition = + viewInfo.tabListPosition ?? DEFAULT_TAB_LIST_POSITION; + + const blockDirection = isBlockDirection(tabListPosition); + const startDirection = isStartDirection(tabListPosition); return ( id) + .join(",")}`} closeButton={viewInfo.closeButton ?? this.closeButton} - direction={viewInfo.tabDirection} disabled={viewInfo.disabled} dragOutside={dragOutsideEnabled} model={viewInfo.widgets} @@ -516,6 +540,7 @@ export class ChFlexibleLayout { showCaptions={viewInfo.showCaptions} sortable={sortableEnabled} tabButtonHidden={viewInfo.tabButtonHidden} + tabListPosition={tabListPosition} // onExpandMainGroup={tabType === "main" ? this.handleMainGroupExpand : null} onItemDragStart={ dragOutsideEnabled && sortableEnabled @@ -543,6 +568,8 @@ export class ChFlexibleLayout { componentWillRender() { this.#leafs = this.#getAllLeafs(); + + // TODO: Find a better life cycle to run this? this.#computePartsToExport(); } @@ -567,7 +594,7 @@ export class ChFlexibleLayout { diff --git a/src/components/flexible-layout/internal/flexible-layout/types.ts b/src/components/flexible-layout/internal/flexible-layout/types.ts index e703fe352..67901fe31 100644 --- a/src/components/flexible-layout/internal/flexible-layout/types.ts +++ b/src/components/flexible-layout/internal/flexible-layout/types.ts @@ -10,10 +10,12 @@ import { LayoutSplitterLeafModel, LayoutSplitterItemRemoveResult } from "../../../layout-splitter/types"; +import { TabListPosition } from "../../../tab/types"; // - - - - - - - - - - - - - - - - - - - - // Input model // - - - - - - - - - - - - - - - - - - - - +// TODO: Remove this unused type export type ViewType = | "inlineStart" | "main" @@ -87,13 +89,12 @@ export type FlexibleLayoutLeafConfigurationTabbed = { /** `true` to not render the tab captions of the view. */ tabButtonHidden?: boolean; - tabDirection: FlexibleLayoutLeafTabDirection; - /** - * Specifies whether the tab is displayed before or after of its content. - * If not specified, defaults to `"start"` + * Specifies the position of the tab list in the tabbed view. + * If not specified, defaults to `"block-start"`. */ - tabPosition?: FlexibleLayoutLeafTabPosition; + tabListPosition?: TabListPosition; + type: Extract; widgets: FlexibleLayoutWidget[]; }; @@ -105,9 +106,6 @@ export type FlexibleLayoutLeafConfigurationSingleContent = { export type FlexibleLayoutLeafType = "tabbed" | "single-content"; -export type FlexibleLayoutLeafTabDirection = "block" | "inline"; -export type FlexibleLayoutLeafTabPosition = "start" | "end"; - export type FlexibleLayoutGroupModel = Omit< LayoutSplitterGroupModel, "items" @@ -228,8 +226,6 @@ export type FlexibleLayoutLeafInfo = { * Same as the leaf id (item.id). */ id: string; - - exportParts: string; } & (T extends "tabbed" ? Required : FlexibleLayoutLeafConfigurationSingleContent); diff --git a/src/components/flexible-layout/tests/slots.e2e.ts b/src/components/flexible-layout/tests/slots.e2e.ts index 75780ead3..3fe282806 100644 --- a/src/components/flexible-layout/tests/slots.e2e.ts +++ b/src/components/flexible-layout/tests/slots.e2e.ts @@ -64,7 +64,7 @@ const SLOT_AND_RENDER_MODEL = { } satisfies FlexibleLayoutModel; const FLEXIBLE_LAYOUT_RENDERED_CONTENT = (children: string) => - `${children}`; + `${children}`; const SLOT_CONTENT = (id: string) => ``; diff --git a/src/components/flexible-layout/utils.ts b/src/components/flexible-layout/utils.ts index f35c31f74..ebcb6d74b 100644 --- a/src/components/flexible-layout/utils.ts +++ b/src/components/flexible-layout/utils.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { tabTypeToPart } from "../tab/utils"; +import { Build } from "@stencil/core"; import { FlexibleLayoutModel, FlexibleLayoutGroupModel, @@ -11,7 +11,7 @@ import { FlexibleLayoutWidgetExtended } from "./internal/flexible-layout/types"; import { ROOT_VIEW } from "../../common/utils"; -import { Build } from "@stencil/core"; +import { DEFAULT_TAB_LIST_POSITION, isBlockDirection } from "../tab/utils"; // Aliases type ItemExtended = FlexibleLayoutItemExtended< @@ -49,22 +49,12 @@ export const createAndSetLeafInfo = ( return { id: leafId, type: leafType, - exportParts: "", widget: widget }; } let selectedWidgetId = flexibleLayoutLeaf.selectedWidgetId; const widgets = flexibleLayoutLeaf.widgets; - const tabOrientation = flexibleLayoutLeaf.tabDirection; - const tabPosition = flexibleLayoutLeaf.tabPosition; - - const exportParts = - tabTypeToPart[ - `${tabOrientation}-${ - tabPosition ?? "start" - }` as keyof typeof tabTypeToPart - ](widgets); widgets.forEach(widget => { if (widget.wasRendered || selectedWidgetId === widget.id) { @@ -87,7 +77,13 @@ export const createAndSetLeafInfo = ( // If there is no widget selected by default, select one if (selectedWidgetId == null && widgets.length > 0) { const selectedWidget = - widgets[tabOrientation === "block" ? widgets.length - 1 : 0]; + widgets[ + isBlockDirection( + flexibleLayoutLeaf.tabListPosition ?? DEFAULT_TAB_LIST_POSITION + ) + ? widgets.length - 1 + : 0 + ]; selectedWidgetId = selectedWidget.id; selectedWidget.wasRendered = true; @@ -97,16 +93,14 @@ export const createAndSetLeafInfo = ( return { id: leafId, - exportParts, closeButton: flexibleLayoutLeaf.closeButton, disabled: flexibleLayoutLeaf.disabled, dragOutside: flexibleLayoutLeaf.dragOutside, selectedWidgetId: selectedWidgetId, showCaptions: flexibleLayoutLeaf.showCaptions ?? true, sortable: flexibleLayoutLeaf.sortable, - tabDirection: tabOrientation, + tabListPosition: flexibleLayoutLeaf.tabListPosition, tabButtonHidden: flexibleLayoutLeaf.tabButtonHidden ?? false, - tabPosition: flexibleLayoutLeaf.tabPosition, type: leafType, widgets: widgets }; diff --git a/src/components/layout-splitter/layout-splitter.tsx b/src/components/layout-splitter/layout-splitter.tsx index 90376a446..2112b9020 100644 --- a/src/components/layout-splitter/layout-splitter.tsx +++ b/src/components/layout-splitter/layout-splitter.tsx @@ -39,6 +39,7 @@ import { import { NO_FIXED_SIZES_TO_UPDATE, removeItem } from "./remove-item"; import { addSiblingLeaf } from "./add-sibling-item"; import { SyncWithRAF } from "../../common/sync-with-frames"; +import { LAYOUT_SPLITTER_PARTS_DICTIONARY } from "../../common/reserved-names"; type Group = LayoutSplitterGroupModel; type Item = LayoutSplitterItemModel; @@ -421,11 +422,16 @@ export class ChLayoutSplitter implements ChComponent { aria-label={this.barAccessibleName} aria-orientation={direction === "columns" ? "vertical" : "horizontal"} aria-valuetext={itemUIModel.actualSize} + // TODO: Add aria-valuenow title={this.barAccessibleName} tabindex="0" // - - - - - - - - - - - - - class="bar" - part={item.dragBar?.part ? `bar ${item.dragBar.part}` : "bar"} + part={ + item.dragBar?.part + ? `${LAYOUT_SPLITTER_PARTS_DICTIONARY.BAR} ${item.dragBar.part}` + : LAYOUT_SPLITTER_PARTS_DICTIONARY.BAR + } style={ item.dragBar?.size ? { "--size": `${item.dragBar.size}px` } : null } diff --git a/src/components/tab/tab.scss b/src/components/tab/tab.scss index 77dece073..5e07dad4b 100644 --- a/src/components/tab/tab.scss +++ b/src/components/tab/tab.scss @@ -30,15 +30,100 @@ $z-index-gx-navbar-item: 107; // Same as $z-index-gx-navbar-item from w-c-l --ch-tab-close-button__background-image-size: 100%; display: grid; - block-size: 100%; } -:host(.ch-tab-direction--block) { - grid-template-rows: max-content 1fr; +:host(.ch-tab--block-start) { + grid-template: + "list" max-content + "panel-container" 1fr / 1fr; } -:host(.ch-tab-direction--inline) { - grid-template-columns: max-content 1fr; +:host(.ch-tab--block-end) { + grid-template: + "panel-container" 1fr + "list" max-content / 1fr; +} + +:host(.ch-tab--inline-start) { + grid-template: "list panel-container" 1fr / max-content 1fr; +} + +:host(.ch-tab--inline-end) { + grid-template: "panel-container list" 1fr / 1fr max-content; +} + +// - - - - - - - - - - - - - - - - - - - - +// Tab lists +// - - - - - - - - - - - - - - - - - - - - +.tab-list { + grid-area: list; + display: grid; + + &--inline { + grid-auto-flow: row; + grid-auto-rows: max-content; + } + + &--block { + grid-auto-flow: column; + grid-auto-columns: max-content; + } +} + +// - - - - - - - - - - - - - - - - - - - - +// Panel container +// - - - - - - - - - - - - - - - - - - - - +.panel-container { + grid-area: panel-container; + display: grid; + grid-template: "selected" 1fr / 1fr; +} + +.panel { + display: grid; + + &--selected { + grid-area: selected; + } +} + +// content-visibility: hidden is faster for rendering the content back, as +// it preserves the rendering state of the content (display: none does not) +@supports (content-visibility: hidden) { + .panel-container--collapsed, + .panel--hidden { + content-visibility: hidden; + } + + .panel-container--collapsed { + inline-size: 0; + } +} + +@supports not (content-visibility: hidden) { + .panel-container--collapsed, + .panel--hidden { + display: none; // Destroys the render state. Ctrl + Z and Ctrl + C won't work properly + } +} + +// - - - - - - - - - - - - - - - - - - - - +// Tab +// - - - - - - - - - - - - - - - - - - - - +.tab { + display: flex; + align-items: center; + justify-content: space-between; + + &:hover > .close-button::after, + &:focus-within > .close-button::after, + &[aria-selected="true"] > .close-button::after { + opacity: 1; + } +} + +.no-captions { + justify-content: center; } .sortable { @@ -73,22 +158,6 @@ $z-index-gx-navbar-item: 107; // Same as $z-index-gx-navbar-item from w-c-l } } -.button { - display: flex; - align-items: center; - justify-content: space-between; - - &:hover > .close-button::after, - &:focus-within > .close-button::after, - &[aria-selected="true"] > .close-button::after { - opacity: 1; - } -} - -.no-captions { - justify-content: center; -} - // - - - - - - - - - - - - - - - - // Drag preview // - - - - - - - - - - - - - - - - @@ -113,6 +182,7 @@ $z-index-gx-navbar-item: 107; // Same as $z-index-gx-navbar-item from w-c-l left: 0; // Necessary since the custom vars are physical values top: 0; // Necessary since the custom vars are physical values z-index: $z-index-gx-navbar-item + 1; + cursor: grabbing; &:popover-open { // Reset popover's browser defaults @@ -132,10 +202,6 @@ $z-index-gx-navbar-item: 107; // Same as $z-index-gx-navbar-item from w-c-l pointer-events: none; } - &-element { - cursor: grabbing; - } - // Drag outside &--outside-tab-list { transform: translate( @@ -173,7 +239,8 @@ $z-index-gx-navbar-item: 107; // Same as $z-index-gx-navbar-item from w-c-l transition: transform var(--ch-tab-transition-duration); } -:host(.ch-tab-direction--block) { +:host(.ch-tab--block-start), +:host(.ch-tab--block-end) { .shifted-element--start { transform: translate(calc(var(--ch-tab-button-size) * -1), 0); } @@ -183,7 +250,8 @@ $z-index-gx-navbar-item: 107; // Same as $z-index-gx-navbar-item from w-c-l } } -:host(.ch-tab-direction--inline) { +:host(.ch-tab--inline-start), +:host(.ch-tab--inline-end) { .shifted-element--start { transform: translateY(calc(var(--ch-tab-button-size) * -1)); } @@ -192,53 +260,3 @@ $z-index-gx-navbar-item: 107; // Same as $z-index-gx-navbar-item from w-c-l transform: translateY(var(--ch-tab-button-size)); } } - -// - - - - - - - - - - - - - - - - -// Tab lists -// - - - - - - - - - - - - - - - - -:host([direction="inline"]) > .tab-list { - display: grid; - grid-auto-flow: row; - grid-auto-rows: max-content; -} - -:host([direction="block"]) > .tab-list { - display: grid; - grid-auto-flow: column; - grid-auto-columns: max-content; -} - -.page { - display: grid; -} - -.page-container { - display: grid; - grid-template: "selected" 1fr / 1fr; - block-size: 100%; - inline-size: 100%; -} - -.page--selected { - grid-area: selected; -} - -// content-visibility: hidden is faster for rendering the content back, as -// it preserves the rendering state of the content (display: none does not) -@supports (content-visibility: hidden) { - .page-container--collapsed, - .page--hidden { - content-visibility: hidden; - } - - .page-container--collapsed { - inline-size: 0; - } -} - -@supports not (content-visibility: hidden) { - .page-container--collapsed, - .page--hidden { - display: none; // Destroys the render state. Ctrl + Z and Ctrl + C won't work properly - } -} diff --git a/src/components/tab/tab.tsx b/src/components/tab/tab.tsx index f7723f5c4..9003cabac 100644 --- a/src/components/tab/tab.tsx +++ b/src/components/tab/tab.tsx @@ -22,25 +22,22 @@ import { tokenMap } from "../../common/utils"; import { - TabDirection, TabElementSize, TabItemCloseInfo, TabItemModel, + TabListPosition, TabModel, TabSelectedItemInfo } from "./types"; import { - CLOSE_BUTTON_PART, + DEFAULT_TAB_LIST_POSITION, DRAG_PREVIEW, - DRAG_PREVIEW_ELEMENT, DRAG_PREVIEW_INSIDE_BLOCK, DRAG_PREVIEW_INSIDE_INLINE, DRAG_PREVIEW_OUTSIDE, - LIST_CLASSES, - LIST_PART_BLOCK, - LIST_PART_INLINE, - PAGE_ID, - SELECTED_PART + isBlockDirection, + isStartDirection, + PANEL_ID } from "./utils"; import { insertIntoIndex, removeElement } from "../../common/array"; import { @@ -49,7 +46,9 @@ import { MouseEventButtons } from "../common/helpers"; import { CssContainProperty, CssOverflowProperty } from "../../common/types"; +import { KEY_CODES, TAB_PARTS_DICTIONARY } from "../../common/reserved-names"; +const TAB_BUTTON_CLASS = "tab"; const CLOSE_BUTTON_CLASS = "close-button"; // Custom vars @@ -70,22 +69,13 @@ const TAB_LIST_EDGE_START_POSITION = "--ch-tab-list-start"; const TAB_LIST_EDGE_END_POSITION = "--ch-tab-list-end"; const DECORATIVE_IMAGE = "--ch-tab-decorative-image"; - -// Key codes -const ARROW_UP = "ArrowUp"; -const ARROW_RIGHT = "ArrowRight"; -const ARROW_DOWN = "ArrowDown"; -const ARROW_LEFT = "ArrowLeft"; -const HOME = "Home"; -const END = "End"; - type KeyEvents = - | typeof ARROW_UP - | typeof ARROW_RIGHT - | typeof ARROW_DOWN - | typeof ARROW_LEFT - | typeof HOME - | typeof END; + | typeof KEY_CODES.ARROW_UP + | typeof KEY_CODES.ARROW_RIGHT + | typeof KEY_CODES.ARROW_DOWN + | typeof KEY_CODES.ARROW_LEFT + | typeof KEY_CODES.HOME + | typeof KEY_CODES.END; // Selectors const FIRST_CAPTION_BUTTON = (tabListRef: HTMLElement) => @@ -96,8 +86,6 @@ const LAST_CAPTION_BUTTON = (tabListRef: HTMLElement) => // Utility functions -const isBlockDirection = (direction: TabDirection) => direction === "block"; - const setProperty = (element: HTMLElement, property: string, value: number) => element.style.setProperty(property, `${value}px`); @@ -136,7 +124,7 @@ const setTabListStartEndPosition = ( const getTabListSizesAndSetPosition = ( hostRef: HTMLChTabRenderElement, tabListRef: HTMLElement, - direction: TabDirection, + blockDirection: boolean, buttonRect: DOMRect ): TabElementSize => { const tabListRect = tabListRef.getBoundingClientRect(); @@ -149,7 +137,7 @@ const getTabListSizesAndSetPosition = ( yEnd: tabListRect.y + tabListRect.height }; - if (isBlockDirection(direction)) { + if (blockDirection) { setTabListStartEndPosition( hostRef, tabListSizes.xStart, @@ -228,24 +216,6 @@ const focusNextOrPreviousCaption = ( export class ChTabRender implements DraggableView { #cancelId: number; - // Styling - #classes: { - BUTTON?: string; - IMAGE?: string; - PAGE?: string; - PAGE_CONTAINER?: string; - PAGE_NAME?: string; - TAB_LIST?: string; - } = {}; - #parts: { - BUTTON?: string; - IMAGE?: string; - PAGE?: string; - PAGE_CONTAINER?: string; - PAGE_NAME?: string; - TAB_LIST?: string; - } = {}; - #selectedIndex: number = -1; #lastDragEvent: MouseEvent; @@ -268,47 +238,47 @@ export class ChTabRender implements DraggableView { #itemIdToIndex: Map = new Map(); // Refs - #dragPreviewRef: HTMLDivElement; + #dragPreviewRef: HTMLButtonElement; #tabListRef: HTMLDivElement; #tabPageRef: HTMLDivElement; // Keyboard interactions #keyEvents: { [key in KeyEvents]: ( - direction: TabDirection, + blockDirection: boolean, event: KeyboardEvent, focusedCaption: HTMLButtonElement ) => void; } = { - [ARROW_UP]: (direction, ev, focusedButton) => { - if (direction === "block") { + [KEY_CODES.ARROW_UP]: (blockDirection, ev, focusedButton) => { + if (blockDirection) { return; } focusNextOrPreviousCaption(false, this.#tabListRef, focusedButton, ev); }, - [ARROW_RIGHT]: (direction, ev, focusedButton) => { - if (direction === "inline") { + [KEY_CODES.ARROW_RIGHT]: (blockDirection, ev, focusedButton) => { + if (!blockDirection) { return; } focusNextOrPreviousCaption(!isRTL(), this.#tabListRef, focusedButton, ev); }, - [ARROW_DOWN]: (direction, ev, focusedButton) => { - if (direction === "block") { + [KEY_CODES.ARROW_DOWN]: (blockDirection, ev, focusedButton) => { + if (blockDirection) { return; } focusNextOrPreviousCaption(true, this.#tabListRef, focusedButton, ev); }, - [ARROW_LEFT]: (direction, ev, focusedButton) => { - if (direction === "inline") { + [KEY_CODES.ARROW_LEFT]: (blockDirection, ev, focusedButton) => { + if (!blockDirection) { return; } focusNextOrPreviousCaption(isRTL(), this.#tabListRef, focusedButton, ev); }, - [HOME]: (_, ev) => + [KEY_CODES.HOME]: (_, ev) => focusNextOrPreviousCaption( true, this.#tabListRef, @@ -316,7 +286,7 @@ export class ChTabRender implements DraggableView { ev ), - [END]: (_, ev) => + [KEY_CODES.END]: (_, ev) => focusNextOrPreviousCaption( false, this.#tabListRef, @@ -365,15 +335,6 @@ export class ChTabRender implements DraggableView { */ @Prop() readonly contain?: CssContainProperty = "none"; - /** - * Specifies the flexible layout type. - */ - @Prop({ reflect: true }) readonly direction: TabDirection; - @Watch("direction") - directionChange(newDirection: TabDirection) { - this.#initializeState(newDirection); - } - /** * This attribute lets you specify if all tab buttons are disabled. * If disabled, tab buttons will not fire any user interaction related event @@ -446,6 +407,11 @@ export class ChTabRender implements DraggableView { */ @Prop() readonly tabButtonHidden: boolean = false; + /** + * Specifies the position of the tab list of the `ch-tab-render`. + */ + @Prop() readonly tabListPosition: TabListPosition = DEFAULT_TAB_LIST_POSITION; + /** * Fired when an item of the main group is double clicked. */ @@ -576,9 +542,9 @@ export class ChTabRender implements DraggableView { // - - - - - - - - - - - DOM read operations - - - - - - - - - - - const mousePositionX = event.clientX; const mousePositionY = event.clientY; - const direction = this.direction; + const blockDirection = isBlockDirection(this.tabListPosition); - const getItemSize = isBlockDirection(direction) + const getItemSize = blockDirection ? (item: HTMLElement) => item.getBoundingClientRect().width : (item: HTMLElement) => item.getBoundingClientRect().height; this.#itemSizes = [...this.#tabListRef.children].map(getItemSize); @@ -591,7 +557,7 @@ export class ChTabRender implements DraggableView { const tabListSizes = getTabListSizesAndSetPosition( this.el, this.#tabListRef, - direction, + blockDirection, buttonRect ); @@ -619,7 +585,7 @@ export class ChTabRender implements DraggableView { } // Store initial mouse position - this.#initialMousePosition = isBlockDirection(direction) + this.#initialMousePosition = blockDirection ? mousePositionX : mousePositionY; @@ -632,7 +598,7 @@ export class ChTabRender implements DraggableView { setButtonSize( this.el, - isBlockDirection(direction) ? buttonRect.width : buttonRect.height + blockDirection ? buttonRect.width : buttonRect.height ); // Update mouse offset to correctly place the dragged element preview @@ -771,7 +737,7 @@ export class ChTabRender implements DraggableView { // In this point, the preview is inside the tab list, we should check // in which place is the preview to give feedback for the item's reorder - const mousePosition = isBlockDirection(this.direction) + const mousePosition = isBlockDirection(this.tabListPosition) ? mousePositionX : mousePositionY; @@ -899,10 +865,14 @@ export class ChTabRender implements DraggableView { } const currentFocusedCaption = focusComposedPath()[0].closest( - "." + this.#classes.BUTTON + "." + TAB_BUTTON_CLASS ) as HTMLButtonElement; - keyEventHandler(this.direction, event, currentFocusedCaption); + keyEventHandler( + isBlockDirection(this.tabListPosition), + event, + currentFocusedCaption + ); }; #getEnabledItems = (): number => { @@ -930,15 +900,17 @@ export class ChTabRender implements DraggableView { item.startImgSrc && ( ); - #renderTabBar = (thereAreShiftedElements: boolean) => { + #renderTabList = (thereAreShiftedElements: boolean) => { + const blockDirection = isBlockDirection(this.tabListPosition); + const startDirection = isStartDirection(this.tabListPosition); const enabledItems = this.#getEnabledItems(); const atLeastOneItemsIsEnabled = enabledItems >= 1; @@ -946,8 +918,19 @@ export class ChTabRender implements DraggableView {
(this.#tabListRef = el)} > {this.model.map((item, index) => - this.#renderTabButton(item, index, thereAreShiftedElements) + this.#renderTabButton( + item, + index, + thereAreShiftedElements, + blockDirection, + startDirection + ) )}
); @@ -985,9 +974,12 @@ export class ChTabRender implements DraggableView { #renderTabButton = ( item: TabItemModel, index: number, - thereAreShiftedElements: boolean + thereAreShiftedElements: boolean, + blockDirection: boolean, + startDirection: boolean ) => { const isDisabled = item.disabled ?? this.disabled; + const selected = item.id === this.selectedId; this.#itemIdToIndex.set(item.id, index); return ( @@ -995,13 +987,15 @@ export class ChTabRender implements DraggableView { key={item.id} id={item.id} role="tab" - aria-controls={PAGE_ID(item.id)} + aria-controls={PANEL_ID(item.id)} aria-label={!this.showCaptions ? item.name : null} - aria-selected={(item.id === this.selectedId).toString()} + aria-selected={selected.toString()} class={{ - [this.#classes.BUTTON]: true, + [TAB_BUTTON_CLASS]: true, "no-captions": !this.showCaptions, + sortable: this.sortable, + "decorative-image": isPseudoElementImg( item.startImgSrc, item.startImgType @@ -1022,15 +1016,21 @@ export class ChTabRender implements DraggableView { "shifted-element--end": thereAreShiftedElements && this.draggedElementNewIndex <= index && - index < this.draggedElementIndex, - - sortable: this.sortable + index < this.draggedElementIndex }} part={tokenMap({ - [this.#parts.BUTTON]: true, [item.id]: true, - [SELECTED_PART]: item.id === this.selectedId, - disabled: isDisabled + [TAB_PARTS_DICTIONARY.TAB]: true, + [this.tabListPosition]: true, + [TAB_PARTS_DICTIONARY.BLOCK]: blockDirection, + [TAB_PARTS_DICTIONARY.INLINE]: !blockDirection, + [TAB_PARTS_DICTIONARY.START]: startDirection, + [TAB_PARTS_DICTIONARY.END]: !startDirection, + [TAB_PARTS_DICTIONARY.CLOSABLE]: this.closeButton, + [TAB_PARTS_DICTIONARY.NOT_CLOSABLE]: !this.closeButton, + [TAB_PARTS_DICTIONARY.SELECTED]: selected, + [TAB_PARTS_DICTIONARY.NOT_SELECTED]: !selected, + [TAB_PARTS_DICTIONARY.DISABLED]: isDisabled })} disabled={isDisabled} style={ @@ -1050,7 +1050,17 @@ export class ChTabRender implements DraggableView { @@ -1059,23 +1069,35 @@ export class ChTabRender implements DraggableView { ); }; - #renderTabPages = () => ( + #renderTabPages = (blockDirection: boolean, startDirection: boolean) => (
(this.#tabPageRef = el)} > - {[...this.#renderedPages.values()].map(this.#renderTabPage)} + {[...this.#renderedPages.values()].map(this.#renderTabPanel)}
); - #renderTabPage = (item: TabItemModel) => { + #renderTabPanel = (item: TabItemModel) => { + // TODO: Avoid this check as much as possible + const blockDirection = isBlockDirection(this.tabListPosition); + const startDirection = isStartDirection(this.tabListPosition); + const contain = item.contain ?? this.contain; + const isDisabled = item.disabled ?? this.disabled; const overflow = item.overflow ?? this.overflow; + const selected = item.id === this.selectedId; const hasContain = contain !== "none"; const hasOverflow = @@ -1083,14 +1105,14 @@ export class ChTabRender implements DraggableView { return (
@@ -1108,16 +1141,9 @@ export class ChTabRender implements DraggableView { }; #renderDragPreview = (draggedElement: TabItemModel) => { - const classes = { - [DRAG_PREVIEW]: true, - [DRAG_PREVIEW_OUTSIDE]: this.hasCrossedBoundaries, - - [DRAG_PREVIEW_INSIDE_INLINE]: - !this.hasCrossedBoundaries && !isBlockDirection(this.direction), - - [DRAG_PREVIEW_INSIDE_BLOCK]: - !this.hasCrossedBoundaries && isBlockDirection(this.direction) - }; + const blockDirection = isBlockDirection(this.tabListPosition); + const startDirection = isStartDirection(this.tabListPosition); + const selected = draggedElement.id === this.selectedId; const decorativeImage = isPseudoElementImg( draggedElement.startImgSrc, @@ -1125,52 +1151,58 @@ export class ChTabRender implements DraggableView { ); return ( - + {this.#imgRender(draggedElement)} + + {this.showCaptions && draggedElement.name} + ); }; - #initializeState = (direction: TabDirection) => { + #initializeState = () => { this.#updateRenderedPages(this.model); - - // Initialize classes and parts - this.#setClassesAndParts(direction); - }; - - #setClassesAndParts = (direction: TabDirection) => { - this.#classes = LIST_CLASSES; - this.#parts = direction === "block" ? LIST_PART_BLOCK : LIST_PART_INLINE; }; + // TODO: Use connectedCallback componentWillLoad() { - this.#initializeState(this.direction); + this.#initializeState(); } render() { @@ -1188,14 +1220,15 @@ export class ChTabRender implements DraggableView { return ( {!this.tabButtonHidden && - this.#renderTabBar(thereAreShiftedElementsInPreview)} - {this.#renderTabPages()} + this.#renderTabList(thereAreShiftedElementsInPreview)} + {this.#renderTabPages( + isBlockDirection(this.tabListPosition), + isStartDirection(this.tabListPosition) + )} {draggedIndex !== -1 && this.#renderDragPreview(draggedElement)} diff --git a/src/components/tab/types.ts b/src/components/tab/types.ts index 914fa5e95..78353f6d4 100644 --- a/src/components/tab/types.ts +++ b/src/components/tab/types.ts @@ -4,7 +4,11 @@ import { ImageRender } from "../../common/types"; -export type TabDirection = "block" | "inline"; +export type TabListPosition = + | "block-start" + | "inline-end" + | "block-end" + | "inline-start"; export type TabModel = TabItemModel[]; diff --git a/src/components/tab/utils.ts b/src/components/tab/utils.ts index 5d8e52704..a522f236e 100644 --- a/src/components/tab/utils.ts +++ b/src/components/tab/utils.ts @@ -1,86 +1,19 @@ -import { - FlexibleLayoutLeafTabDirection, - FlexibleLayoutLeafTabPosition, - FlexibleLayoutWidget -} from "../flexible-layout/internal/flexible-layout/types"; +import { TabListPosition } from "./types"; -// Classes and parts -export const BUTTON_CLASS = "button"; -export const IMAGE_CLASS = "img"; -export const PAGE_CLASS = "page"; -export const PAGE_CONTAINER_CLASS = "page-container"; -export const PAGE_NAME_CLASS = "page-name"; -export const TAB_LIST_CLASS = "tab-list"; - -export const LIST_CLASSES = { - BUTTON: BUTTON_CLASS, - IMAGE: IMAGE_CLASS, - PAGE: PAGE_CLASS, - PAGE_CONTAINER: PAGE_CONTAINER_CLASS, - PAGE_NAME: PAGE_NAME_CLASS, - TAB_LIST: TAB_LIST_CLASS -}; +// Ids +export const PANEL_ID = (name: string) => `panel-${name}`; -export const LIST_PART_BLOCK = { - BUTTON: BUTTON_CLASS + " block", - IMAGE: IMAGE_CLASS + " block", - PAGE: PAGE_CLASS + " block", - PAGE_CONTAINER: PAGE_CONTAINER_CLASS + " block", - PAGE_NAME: PAGE_NAME_CLASS + " block", - TAB_LIST: TAB_LIST_CLASS + " block" -}; +export const isBlockDirection = (direction: TabListPosition) => + direction === "block-start" || direction === "block-end"; -export const LIST_PART_INLINE = { - BUTTON: BUTTON_CLASS + " inline", - IMAGE: IMAGE_CLASS + " inline", - PAGE: PAGE_CLASS + " inline", - PAGE_CONTAINER: PAGE_CONTAINER_CLASS + " inline", - PAGE_NAME: PAGE_NAME_CLASS + " inline", - TAB_LIST: TAB_LIST_CLASS + " inline" -}; +export const isStartDirection = (direction: TabListPosition) => + direction === "block-start" || direction === "inline-start"; -// Ids -export const PAGE_ID = (name: string) => `page-${name}`; +export const DEFAULT_TAB_LIST_POSITION = "block-start"; // Export part functions -export const CLOSE_BUTTON_PART = "close-button"; export const DRAG_PREVIEW = "drag-preview"; -export const DRAG_PREVIEW_ELEMENT = "drag-preview-element"; export const DRAG_PREVIEW_OUTSIDE = "drag-preview--outside-tab-list"; export const DRAG_PREVIEW_INSIDE_BLOCK = "drag-preview--inside-tab-list__block"; export const DRAG_PREVIEW_INSIDE_INLINE = "drag-preview--inside-tab-list__inline"; -export const SELECTED_PART = "selected"; - -const partsArray = [ - ...Object.values(LIST_CLASSES), - CLOSE_BUTTON_PART, - DRAG_PREVIEW, - DRAG_PREVIEW_ELEMENT, - DRAG_PREVIEW_OUTSIDE, - DRAG_PREVIEW_INSIDE_BLOCK, - DRAG_PREVIEW_INSIDE_INLINE, - SELECTED_PART -]; - -const PARTS = partsArray.join(","); - -export const INLINE_START_PARTS = PARTS + ",inline,inline:start"; -export const INLINE_END_PARTS = PARTS + ",inline,inline:end"; - -export const BLOCK_START_PARTS = PARTS + ",block,block:start"; -export const BLOCK_END_PARTS = PARTS + ",block,block:end"; - -export const CAPTION_PARTS = (widgets: FlexibleLayoutWidget[]) => - widgets.map(item => item.id).join(","); - -export const tabTypeToPart: { - [key in `${FlexibleLayoutLeafTabDirection}-${FlexibleLayoutLeafTabPosition}`]: ( - widgets: FlexibleLayoutWidget[] - ) => string; -} = { - "inline-start": widgets => INLINE_START_PARTS + "," + CAPTION_PARTS(widgets), - "inline-end": widgets => INLINE_END_PARTS + "," + CAPTION_PARTS(widgets), - "block-start": widgets => BLOCK_START_PARTS + "," + CAPTION_PARTS(widgets), - "block-end": widgets => BLOCK_END_PARTS + "," + CAPTION_PARTS(widgets) -} as const; diff --git a/src/components/test/test-flexible-layout/renders.tsx b/src/components/test/test-flexible-layout/renders.tsx index 45bf52b18..b1d88a35b 100644 --- a/src/components/test/test-flexible-layout/renders.tsx +++ b/src/components/test/test-flexible-layout/renders.tsx @@ -11,14 +11,12 @@ import { kbExplorerModel, preferencesModel } from "../../../showcase/assets/components/tree-view/models"; -import { GXWebModel } from "../../../showcase/pages/assets/models/action-group.js"; import { TreeViewItemModel } from "../../tree-view/types"; import { ChTreeViewRenderCustomEvent, ThemeModel, TreeViewItemOpenReferenceInfo } from "../../../components"; -import { panelToolbox } from "../../../showcase/assets/components/action-list/models"; const UNANIMO_THEME: ThemeModel = [ { @@ -43,7 +41,7 @@ const MERCURY_THEME: ThemeModel = [ ]; // IDs -const MENU_BAR = "menu-bar"; +export const MENU_BAR = "menu-bar"; const KB_EXPLORER = "kb-explorer"; const PREFERENCES = "preferences"; const HEAVY_TREE = "heavy-tree"; @@ -51,7 +49,7 @@ const START_PAGE = "start-page"; const STRUCT_EDITOR = "StructEditor"; const ATTRS_CONTAINERS_AND_OTHERS = "AttrsContainersAndOthers"; const PROPERTIES = "properties"; -const TOOLBOX = "toolbox"; +export const TOOLBOX = "toolbox"; const OUTPUT = "output"; const IMPORT_OBJECTS = "import-objects"; const PANEL1 = "panel-1"; @@ -73,7 +71,8 @@ export const defaultLayout: FlexibleLayoutModel = { type: "single-content", widget: { id: MENU_BAR, - name: null + name: null, + slot: true } }, { @@ -86,7 +85,7 @@ export const defaultLayout: FlexibleLayoutModel = { accessibleRole: "complementary", size: "300px", type: "tabbed", - tabDirection: "inline", + tabListPosition: "inline-start", selectedWidgetId: KB_EXPLORER, widgets: [ { @@ -114,7 +113,6 @@ export const defaultLayout: FlexibleLayoutModel = { accessibleRole: "main", size: "1fr", type: "tabbed", - tabDirection: "block", selectedWidgetId: START_PAGE, widgets: [ { id: START_PAGE, name: "Start Page" }, @@ -130,8 +128,7 @@ export const defaultLayout: FlexibleLayoutModel = { accessibleRole: "complementary", size: "300px", type: "tabbed", - tabDirection: "inline", - tabPosition: "end", + tabListPosition: "inline-start", widgets: [ { id: PROPERTIES, @@ -147,7 +144,6 @@ export const defaultLayout: FlexibleLayoutModel = { accessibleRole: "contentinfo", size: "200px", type: "tabbed", - tabDirection: "block", widgets: [ { id: OUTPUT, @@ -171,7 +167,8 @@ export const layout2: FlexibleLayoutModel = { type: "single-content", widget: { id: MENU_BAR, - name: null + name: null, + slot: true } }, { @@ -184,7 +181,7 @@ export const layout2: FlexibleLayoutModel = { accessibleRole: "complementary", size: "300px", type: "tabbed", - tabDirection: "inline", + tabListPosition: "inline-start", selectedWidgetId: KB_EXPLORER, widgets: [ { @@ -217,7 +214,6 @@ export const layout2: FlexibleLayoutModel = { id: "sub-group-2-2-1", size: "1fr", type: "tabbed", - tabDirection: "block", selectedWidgetId: START_PAGE, widgets: [{ id: START_PAGE, name: "Start Page" }] }, @@ -225,7 +221,6 @@ export const layout2: FlexibleLayoutModel = { id: "sub-group-2-2-2", size: "1fr", type: "tabbed", - tabDirection: "block", widgets: [ { id: STRUCT_EDITOR, @@ -241,8 +236,7 @@ export const layout2: FlexibleLayoutModel = { accessibleRole: "complementary", size: "300px", type: "tabbed", - tabDirection: "inline", - tabPosition: "end", + tabListPosition: "inline-end", widgets: [ { id: PROPERTIES, @@ -258,7 +252,6 @@ export const layout2: FlexibleLayoutModel = { accessibleRole: "contentinfo", size: "200px", type: "tabbed", - tabDirection: "block", widgets: [ { id: OUTPUT, @@ -282,7 +275,8 @@ export const layout3: FlexibleLayoutModel = { type: "single-content", widget: { id: MENU_BAR, - name: null + name: null, + slot: true } }, { @@ -299,7 +293,7 @@ export const layout3: FlexibleLayoutModel = { dragBar: { part: "visible", size: 1 }, size: "300px", type: "tabbed", - tabDirection: "inline", + tabListPosition: "inline-start", selectedWidgetId: KB_EXPLORER, showCaptions: false, widgets: [ @@ -342,7 +336,6 @@ export const layout3: FlexibleLayoutModel = { dragBar: { part: "visible", size: 1 }, size: "0.5fr", type: "tabbed", - tabDirection: "block", selectedWidgetId: START_PAGE, widgets: [ { @@ -362,7 +355,6 @@ export const layout3: FlexibleLayoutModel = { dragBar: { part: "visible", size: 1 }, size: "0.5fr", type: "tabbed", - tabDirection: "block", widgets: [ { id: STRUCT_EDITOR, @@ -382,7 +374,6 @@ export const layout3: FlexibleLayoutModel = { id: "sub-group-2-2-1-2-2", size: "0.5fr", type: "tabbed", - tabDirection: "block", widgets: [ { id: PANEL1, @@ -416,7 +407,6 @@ export const layout3: FlexibleLayoutModel = { accessibleRole: "contentinfo", size: "200px", type: "tabbed", - tabDirection: "block", widgets: [ { id: OUTPUT, @@ -434,8 +424,7 @@ export const layout3: FlexibleLayoutModel = { size: "300px", showCaptions: false, type: "tabbed", - tabDirection: "inline", - tabPosition: "end", + tabListPosition: "inline-end", widgets: [ { id: PROPERTIES, @@ -445,7 +434,8 @@ export const layout3: FlexibleLayoutModel = { { id: TOOLBOX, name: "Toolbox", - startImgSrc: `${ASSETS_PREFIX}toolbar/toolbox.svg` + startImgSrc: `${ASSETS_PREFIX}toolbar/toolbox.svg`, + slot: true } ] } @@ -487,7 +477,7 @@ const openNewPanel = ( export const layoutRenders = ( designSystem: "mercury" | "unanimo" ): FlexibleLayoutRenders => ({ - [MENU_BAR]: () => [ + [KB_EXPLORER]: () => [ , - - ], - [KB_EXPLORER]: () => ( - ), + ], [PREFERENCES]: () => ( This is the render for the {widget.id} widget - ), - [TOOLBOX]: () => ( - ) }); diff --git a/src/components/test/test-flexible-layout/test-action-group-styles.scss b/src/components/test/test-flexible-layout/test-action-group-styles.scss index d52117f6c..b8c80c4be 100644 --- a/src/components/test/test-flexible-layout/test-action-group-styles.scss +++ b/src/components/test/test-flexible-layout/test-action-group-styles.scss @@ -1,10 +1,11 @@ .action-group { min-height: 32px; padding-inline: 48px; - background-color: var( - --colors-foundation__gray--300, - var(--mer-color__neutral-gray--600) - ); + background-color: var(--flexible-layout--tab-list-inline__background-color); + + &::part(actions) { + gap: 8px; + } } .action-group::part(more-actions-window), diff --git a/src/components/test/test-flexible-layout/test-flexible-layout.scss b/src/components/test/test-flexible-layout/test-flexible-layout.scss index 8e6288de4..f6c2d4fdf 100644 --- a/src/components/test/test-flexible-layout/test-flexible-layout.scss +++ b/src/components/test/test-flexible-layout/test-flexible-layout.scss @@ -17,95 +17,9 @@ ch-test-flexible-layout { } .flexible-layout { - // &::part(page-container inline) { - // background-color: var(--colors-foundation__gray--200); - // } - - // &::part(button inline) { - // position: relative; - // block-size: 32px; - // inline-size: 48px; - - // &::before { - // display: flex; - // block-size: 100%; - // inline-size: 100%; - // -webkit-mask-size: 16px; - // } - // } - - // &::part(button selected inline) { - // &::after { - // content: ""; - // display: flex; - // position: absolute; - // inset-block-start: 4px; - // inset-inline-start: 1px; - // block-size: calc(100% - 8px); - // inline-size: 2px; - // background-color: currentColor; - // } - // } - - // &::part(button inlineEnd selected)::after { - // inset-inline-start: unset; - // inset-inline-end: 1px; - // } - - // // &::part(page-name inline) { - // // display: block; - // // padding-block: 8px; - // // padding-inline: 16px; - // // } - - // &::part(close-button) { - // padding: 1px; - - // &:hover { - // background-color: color-mix(in srgb, currentColor 10%, transparent); - // border-radius: 4px; - // } - - // &:focus-visible { - // outline: 1px solid currentColor; - // } - // } - - // &::part(button inline):focus-visible { - // outline: 1px solid currentColor; - // outline-offset: -1px; - // } - - // // Main, BlockEnd - // &::part(button block) { - // padding-block: 6px; - // padding-inline: 16px 8px; - // gap: 8px; - // background-color: var(--colors-foundation__gray--200); - - // &:focus-visible { - // outline: 1px solid currentColor; - // outline-offset: -1px; - // } - // } - - // &::part(button block selected) { - // background-color: var(--elevation--2); - // } - - // &::part(button block selected blockEnd) { - // position: relative; - - // &::after { - // content: ""; - // position: absolute; - // inset-block-start: 0; - // inset-inline: 0; - // block-size: 1px; - // background-color: currentColor; - // z-index: 1; - // } - // } + &::part(leaf inline) { + background-color: var(--mer-surface__elevation--01); + } &::part(tab-list block) { overflow: auto; @@ -114,11 +28,11 @@ ch-test-flexible-layout { // - - - - - - - - - - - - - - - - // Drag preview // - - - - - - - - - - - - - - - - - &::part(drag-preview-element button block) { - padding-inline: 25px; - } + // &::part(drag-preview-element button block) { + // padding-inline: 25px; + // } - &::part(drag-preview--outside-tab-list) { - box-shadow: 0 3px 4px 1px #00000020; - } + // &::part(drag-preview--outside-tab-list) { + // box-shadow: 0 3px 4px 1px #00000020; + // } } diff --git a/src/components/test/test-flexible-layout/test-flexible-layout.tsx b/src/components/test/test-flexible-layout/test-flexible-layout.tsx index e16d2704b..768e735b6 100644 --- a/src/components/test/test-flexible-layout/test-flexible-layout.tsx +++ b/src/components/test/test-flexible-layout/test-flexible-layout.tsx @@ -1,7 +1,9 @@ import { Component, Prop, h } from "@stencil/core"; import { FlexibleLayoutModel } from "../../flexible-layout/internal/flexible-layout/types"; // import { defaultLayout, layout2, layoutRenders } from "./renders"; -import { layout3, layoutRenders } from "./renders"; +import { layout3, layoutRenders, MENU_BAR, TOOLBOX } from "./renders"; +import { panelToolbox } from "../../../showcase/assets/components/action-list/models"; +import { GXWebModel } from "../../../showcase/assets/components/action-group/models"; @Component({ shadow: false, @@ -29,7 +31,19 @@ export class ChTestFlexibleLayout { model={this.model} renders={layoutRenders(this.designSystem)} sortable - > + > + + + ); } } diff --git a/src/showcase/assets/components/accordion/accordion.showcase.tsx b/src/showcase/assets/components/accordion/accordion.showcase.tsx index 4df50a538..226d09b7f 100644 --- a/src/showcase/assets/components/accordion/accordion.showcase.tsx +++ b/src/showcase/assets/components/accordion/accordion.showcase.tsx @@ -1,4 +1,4 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseRenderProperties, ShowcaseStory, @@ -17,7 +17,8 @@ import { import { insertSpacesAtTheBeginningExceptForTheFirstLine, renderShowcaseProperties, - showcaseTemplateClassProperty + showcaseTemplateClassProperty, + updateShowcase } from "../utils"; const state: Partial = {}; @@ -34,11 +35,7 @@ const expandedItemChangeHandler = ( // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = event.target.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( diff --git a/src/showcase/assets/components/barcode-scanner/barcode-scanner.showcase.tsx b/src/showcase/assets/components/barcode-scanner/barcode-scanner.showcase.tsx index cad4c0051..babb1133f 100644 --- a/src/showcase/assets/components/barcode-scanner/barcode-scanner.showcase.tsx +++ b/src/showcase/assets/components/barcode-scanner/barcode-scanner.showcase.tsx @@ -1,14 +1,13 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ChBarcodeScannerCustomEvent } from "../../../../components"; import { ShowcaseRenderProperties, ShowcaseStory, ShowcaseTemplatePropertyInfo } from "../types"; -import { renderShowcaseProperties } from "../utils"; +import { renderShowcaseProperties, updateShowcase } from "../utils"; const state: Partial = {}; -let barcodeScannerRef: HTMLChBarcodeScannerElement; let lastRead: string; @@ -18,11 +17,7 @@ const handleRead = (event: ChBarcodeScannerCustomEvent) => { // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = barcodeScannerRef.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( @@ -32,7 +27,6 @@ const render = () => ( barcodeBoxHeight={state.barcodeBoxHeight} scanning={state.scanning} onRead={handleRead} - ref={el => (barcodeScannerRef = el)} > Content: {lastRead} diff --git a/src/showcase/assets/components/checkbox/checkbox.showcase.tsx b/src/showcase/assets/components/checkbox/checkbox.showcase.tsx index d15ac714c..eefd50ff1 100644 --- a/src/showcase/assets/components/checkbox/checkbox.showcase.tsx +++ b/src/showcase/assets/components/checkbox/checkbox.showcase.tsx @@ -1,10 +1,10 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseRenderProperties, ShowcaseStory, ShowcaseTemplatePropertyInfo } from "../types"; -import { renderShowcaseProperties } from "../utils"; +import { renderShowcaseProperties, updateShowcase } from "../utils"; const state: Partial = {}; const formRefs: { @@ -33,11 +33,7 @@ const handleValueInput = // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = formRefs[formId].closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( diff --git a/src/showcase/assets/components/combo-box/combo-box.showcase.tsx b/src/showcase/assets/components/combo-box/combo-box.showcase.tsx index d81327aa9..aab7d8587 100644 --- a/src/showcase/assets/components/combo-box/combo-box.showcase.tsx +++ b/src/showcase/assets/components/combo-box/combo-box.showcase.tsx @@ -1,4 +1,4 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseRenderProperties, ShowcaseRenderProperty, @@ -17,7 +17,7 @@ import { ComboBoxItemModel } from "../../../../components/combo-box/types"; import { ChComboBoxRenderCustomEvent } from "../../../../components"; -import { renderShowcaseProperties } from "../utils"; +import { renderShowcaseProperties, updateShowcase } from "../utils"; const state: Partial = {}; let itemsFilteredByTheServer: ComboBoxItemModel[] = comboBoxFilterChange({ @@ -67,13 +67,7 @@ const handleInputChange = // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = ( - event as ChComboBoxRenderCustomEvent - ).target.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const handleChangeEvent = @@ -83,13 +77,7 @@ const handleChangeEvent = // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = ( - event as ChComboBoxRenderCustomEvent - ).target.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( diff --git a/src/showcase/assets/components/dialog/dialog.showcase.tsx b/src/showcase/assets/components/dialog/dialog.showcase.tsx index 7be64b707..ce36fdc07 100644 --- a/src/showcase/assets/components/dialog/dialog.showcase.tsx +++ b/src/showcase/assets/components/dialog/dialog.showcase.tsx @@ -1,45 +1,35 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseRenderProperties, ShowcaseStory, ShowcaseTemplateFrameWork, ShowcaseTemplatePropertyInfo } from "../types"; -import { ChDialogCustomEvent } from "../../../../components"; import { insertSpacesAtTheBeginningExceptForTheFirstLine, renderShowcaseProperties, - showcaseTemplateClassProperty + showcaseTemplateClassProperty, + updateShowcase } from "../utils"; const state: Partial = {}; -const handleClose = (event: ChDialogCustomEvent) => { +const handleClose = () => { state.hidden = true; // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = event.target.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; -const handleDialogOpen = (event: MouseEvent) => { +const handleDialogOpen = () => { state.hidden = false; // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = (event.target as HTMLButtonElement).closest( - "ch-showcase" - ); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => [ diff --git a/src/showcase/assets/components/edit/edit.showcase.tsx b/src/showcase/assets/components/edit/edit.showcase.tsx index 7ef2ca0b6..314a4acca 100644 --- a/src/showcase/assets/components/edit/edit.showcase.tsx +++ b/src/showcase/assets/components/edit/edit.showcase.tsx @@ -1,4 +1,4 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseRenderProperties, ShowcaseStory, @@ -6,7 +6,8 @@ import { } from "../types"; import { renderShowcaseProperties, - showcaseTemplateClassProperty + showcaseTemplateClassProperty, + updateShowcase } from "../utils"; import { dummyPictureCallback } from "./models"; @@ -37,11 +38,7 @@ const handleValueInput = // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = formRefs[formId].closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( diff --git a/src/showcase/assets/components/markdown/markdown.showcase.tsx b/src/showcase/assets/components/markdown/markdown.showcase.tsx index c1a01d8a9..ac72525dd 100644 --- a/src/showcase/assets/components/markdown/markdown.showcase.tsx +++ b/src/showcase/assets/components/markdown/markdown.showcase.tsx @@ -1,6 +1,7 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseCustomStory, ShowcaseRender } from "../types"; import { markdownReadmeModel } from "./models"; +import { updateShowcase } from "../utils"; let initialMarkdown = markdownReadmeModel; let rawHTMLEnabled = "false"; @@ -16,11 +17,7 @@ const handleValueChange = () => { // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = textareaRef.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const handleCheckboxValueChange = () => { @@ -30,11 +27,7 @@ const handleCheckboxValueChange = () => { // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = checkboxRawHTMLRef.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render: ShowcaseRender = designSystem => ( diff --git a/src/showcase/assets/components/popover/popover.showcase.tsx b/src/showcase/assets/components/popover/popover.showcase.tsx index eeda28d14..5d135da92 100644 --- a/src/showcase/assets/components/popover/popover.showcase.tsx +++ b/src/showcase/assets/components/popover/popover.showcase.tsx @@ -1,4 +1,4 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseRenderProperties, ShowcaseStory, @@ -6,12 +6,12 @@ import { } from "../types"; import { renderShowcaseProperties, - showcaseTemplateClassProperty + showcaseTemplateClassProperty, + updateShowcase } from "../utils"; const state: Partial = {}; let buttonRef: HTMLButtonElement; -let popoverRef: HTMLChPopoverElement; const handlePopoverOpened = () => { state.hidden = false; @@ -19,11 +19,7 @@ const handlePopoverOpened = () => { // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = popoverRef.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const handlePopoverClosed = () => { @@ -32,11 +28,7 @@ const handlePopoverClosed = () => { // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = popoverRef.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( @@ -62,7 +54,6 @@ const render = () => ( resizable={state.resizable} onPopoverOpened={handlePopoverOpened} onPopoverClosed={handlePopoverClosed} - ref={el => (popoverRef = el)} >
= {}; const formRefs: { @@ -33,11 +33,7 @@ const handleValueInput = // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = formRefs[formId].closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( diff --git a/src/showcase/assets/components/renders.ts b/src/showcase/assets/components/renders.ts index ce0e6188a..92de37a01 100644 --- a/src/showcase/assets/components/renders.ts +++ b/src/showcase/assets/components/renders.ts @@ -57,7 +57,6 @@ export const flexibleLayoutPlaygroundConfiguration: FlexibleLayoutModel = { size: "1fr", minSize: "220px", selectedWidgetId: MAIN_WIDGET, - tabDirection: "block", type: "tabbed", widgets: [ { id: MAIN_WIDGET, name: "Playground" }, diff --git a/src/showcase/assets/components/switch/switch.showcase.tsx b/src/showcase/assets/components/switch/switch.showcase.tsx index 87ca18bc4..10872ab01 100644 --- a/src/showcase/assets/components/switch/switch.showcase.tsx +++ b/src/showcase/assets/components/switch/switch.showcase.tsx @@ -1,10 +1,10 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseRenderProperties, ShowcaseStory, ShowcaseTemplatePropertyInfo } from "../types"; -import { renderShowcaseProperties } from "../utils"; +import { renderShowcaseProperties, updateShowcase } from "../utils"; const state: Partial = {}; const formRefs: { @@ -32,11 +32,7 @@ const handleValueInput = // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = formRefs[formId].closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( diff --git a/src/showcase/assets/components/tab/tab.showcase.tsx b/src/showcase/assets/components/tab/tab.showcase.tsx index 00ee72507..c495b9c2e 100644 --- a/src/showcase/assets/components/tab/tab.showcase.tsx +++ b/src/showcase/assets/components/tab/tab.showcase.tsx @@ -1,4 +1,4 @@ -import { forceUpdate, h } from "@stencil/core"; +import { h } from "@stencil/core"; import { ShowcaseRenderProperties, ShowcaseStory, @@ -23,7 +23,8 @@ import { import { insertSpacesAtTheBeginningExceptForTheFirstLine, renderShowcaseProperties, - showcaseTemplateClassProperty + showcaseTemplateClassProperty, + updateShowcase } from "../utils"; const state: Partial = {}; @@ -38,11 +39,7 @@ const selectedItemChangeHandler = ( // TODO: Until we support external slots in the ch-flexible-layout-render, // this is a hack to update the render of the widget and thus re-render the // combo-box updating the displayed items - const showcaseRef = event.target.closest("ch-showcase"); - - if (showcaseRef) { - forceUpdate(showcaseRef); - } + updateShowcase(); }; const render = () => ( @@ -55,7 +52,7 @@ const render = () => ( accessibleName={state.accessibleName} closeButton={state.closeButton} closeButtonAccessibleName={state.closeButtonAccessibleName} - direction={state.direction} + tabListPosition={state.tabListPosition} disabled={state.disabled} dragOutside={state.dragOutside} expanded={state.expanded} @@ -103,7 +100,7 @@ const render = () => ( accessibleName={state.accessibleName} closeButton={state.closeButton} closeButtonAccessibleName={state.closeButtonAccessibleName} - direction={state.direction} + tabListPosition={state.tabListPosition} disabled={state.disabled} dragOutside={state.dragOutside} expanded={state.expanded} @@ -176,12 +173,14 @@ const showcaseRenderProperties: ShowcaseRenderProperties caption: "Properties", properties: [ { - id: "direction", - caption: "Direction", - value: "block", + id: "tabListPosition", + caption: "Tab List Position", + value: "block-start", values: [ - { caption: "Block", value: "block" }, - { caption: "Inline", value: "inline" } + { caption: "Block Start", value: "block-start" }, + { caption: "Block End", value: "block-end" }, + { caption: "Inline Start", value: "inline-start" }, + { caption: "Inline End", value: "inline-end" } ], render: "radio-group", type: "enum" diff --git a/src/showcase/assets/components/utils.ts b/src/showcase/assets/components/utils.ts index 321ce3f1c..538bc06ba 100644 --- a/src/showcase/assets/components/utils.ts +++ b/src/showcase/assets/components/utils.ts @@ -1,9 +1,17 @@ +import { forceUpdate } from "@stencil/core"; import { ShowcaseTemplateFrameWork, ShowcaseTemplatePropertyInfo, ShowcaseTemplatePropertyInfoVariable } from "./types"; +export const updateShowcase = () => { + const showcaseRef = document.querySelector("ch-showcase"); + if (showcaseRef) { + forceUpdate(showcaseRef); + } +}; + export const insertSpacesAtTheBeginningExceptForTheFirstLine = ( text: string, spaces = 2