Skip to content

Commit

Permalink
[ch-tab-render][ch-flexible-layout-render] Remove the direction p…
Browse files Browse the repository at this point in the history
…roperty 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
  • Loading branch information
ncamera authored Dec 3, 2024
1 parent 76b22fb commit e470c71
Show file tree
Hide file tree
Showing 29 changed files with 539 additions and 636 deletions.
62 changes: 60 additions & 2 deletions src/common/reserved-names.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
// - - - - - - - - - - - - - - - - - - - -
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/components/flexible-layout/flexible-layout-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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}"]`;

Expand Down Expand Up @@ -464,50 +475,63 @@ export class ChFlexibleLayout {
};

#computePartsToExport = () => {
const exportPartsSet = new Set([
"bar",
"block",
"inline",
"leaf",
"start",
"end"
const exportPartsSet = new Set<string>([
...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 (
<ch-tab-render
id={viewInfo.id}
key={viewInfo.id}
slot={viewInfo.id}
contain={this.contain}
class={{
[`ch-tab-${viewInfo.tabDirection}--end`]:
viewInfo.tabPosition === "end"
}}
part={`leaf ${viewInfo.tabDirection} ${
viewInfo.tabPosition ?? "start"
} ${viewInfo.id}`}
exportparts={viewInfo.exportParts}
// TODO: Add hostParts property in the ch-tab-render
part={tokenMap({
[viewInfo.id]: true,
[FLEXIBLE_LAYOUT_PARTS_DICTIONARY.LEAF]: true,
[tabListPosition]: true,
[TAB_PARTS_DICTIONARY.BLOCK]: blockDirection,
[TAB_PARTS_DICTIONARY.INLINE]: !blockDirection,
[TAB_PARTS_DICTIONARY.START]: startDirection,
[TAB_PARTS_DICTIONARY.END]: !startDirection
})}
// TODO: Find a better way to avoid this mapping on every render
exportparts={`${TAB_EXPORT_PARTS},${tabListPosition},${viewInfo.widgets
.map(({ id }) => id)
.join(",")}`}
closeButton={viewInfo.closeButton ?? this.closeButton}
direction={viewInfo.tabDirection}
disabled={viewInfo.disabled}
dragOutside={dragOutsideEnabled}
model={viewInfo.widgets}
Expand All @@ -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
Expand Down Expand Up @@ -543,6 +568,8 @@ export class ChFlexibleLayout {

componentWillRender() {
this.#leafs = this.#getAllLeafs();

// TODO: Find a better life cycle to run this?
this.#computePartsToExport();
}

Expand All @@ -567,7 +594,7 @@ export class ChFlexibleLayout {
<div
aria-hidden="true"
class="droppable-area"
part="droppable-area"
part={FLEXIBLE_LAYOUT_PARTS_DICTIONARY.DROPPABLE_AREA}
popover="manual"
ref={el => (this.#droppableAreaRef = el)}
></div>
Expand Down
16 changes: 6 additions & 10 deletions src/components/flexible-layout/internal/flexible-layout/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<FlexibleLayoutLeafType, "tabbed">;
widgets: FlexibleLayoutWidget[];
};
Expand All @@ -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"
Expand Down Expand Up @@ -228,8 +226,6 @@ export type FlexibleLayoutLeafInfo<T extends FlexibleLayoutLeafType> = {
* Same as the leaf id (item.id).
*/
id: string;

exportParts: string;
} & (T extends "tabbed"
? Required<FlexibleLayoutLeafConfigurationTabbed>
: FlexibleLayoutLeafConfigurationSingleContent);
Expand Down
2 changes: 1 addition & 1 deletion src/components/flexible-layout/tests/slots.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const SLOT_AND_RENDER_MODEL = {
} satisfies FlexibleLayoutModel;

const FLEXIBLE_LAYOUT_RENDERED_CONTENT = (children: string) =>
`<ch-flexible-layout exportparts="bar,block,inline,leaf,start,end" class="hydrated">${children}</ch-flexible-layout>`;
`<ch-flexible-layout exportparts="tab,close-button,tab-list,tab-panel,tab-panel-container,img,closable,not-closable,disabled,dragging,dragging-over-tab-list,dragging-out-of-tab-list,expanded,collapsed,selected,not-selected,block,inline,start,end,droppable-area,leaf,bar" class="hydrated">${children}</ch-flexible-layout>`;

const SLOT_CONTENT = (id: string) => `<slot name="${id}" slot="${id}"></slot>`;

Expand Down
26 changes: 10 additions & 16 deletions src/components/flexible-layout/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { tabTypeToPart } from "../tab/utils";
import { Build } from "@stencil/core";
import {
FlexibleLayoutModel,
FlexibleLayoutGroupModel,
Expand All @@ -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<
Expand Down Expand Up @@ -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) {
Expand All @@ -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;

Expand All @@ -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
};
Expand Down
8 changes: 7 additions & 1 deletion src/components/layout-splitter/layout-splitter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}
Expand Down
Loading

0 comments on commit e470c71

Please sign in to comment.