Skip to content

Commit

Permalink
feat(ui5-flexible-column-layout): add arrow icon functionality (#10362)
Browse files Browse the repository at this point in the history
* feat(ui5-flexible-column-layout): add arrow icon functionality

* refactor: added the arrow icon in the tsx template

* refactor: improve layout switch

* fix: rewrite test and icon handling

* refactor: improve keyboard and separator handling

* refactor: remove redundant separator checks and improve DOM rendering
  • Loading branch information
NakataCode authored Jan 22, 2025
1 parent 6b7dbf0 commit 61b94ae
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 12 deletions.
77 changes: 74 additions & 3 deletions packages/fiori/src/FlexibleColumnLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { supportsTouch } from "@ui5/webcomponents-base/dist/Device.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import AnimationMode from "@ui5/webcomponents-base/dist/types/AnimationMode.js";
import { getAnimationMode } from "@ui5/webcomponents-base/dist/config/AnimationMode.js";
import Icon from "@ui5/webcomponents/dist/Icon.js";
import Button from "@ui5/webcomponents/dist/Button.js";
import "@ui5/webcomponents-icons/dist/vertical-grip.js";
import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js";
import {
Expand All @@ -19,12 +21,15 @@ import {
isRightShift,
isHome,
isEnd,
isEnter,
isSpace,
} from "@ui5/webcomponents-base/dist/Keys.js";
import type { PassiveEventListenerObject, AriaLandmarkRole } from "@ui5/webcomponents-base";
import FCLLayout from "./types/FCLLayout.js";
import type { LayoutConfiguration } from "./fcl-utils/FCLLayout.js";
import {
getLayoutsByMedia,
getNextLayoutByArrowPress,
} from "./fcl-utils/FCLLayout.js";

// Texts
Expand Down Expand Up @@ -167,6 +172,7 @@ type UserDefinedColumnLayouts = {
renderer: jsxRenderer,
styles: FlexibleColumnLayoutCss,
template: FlexibleColumnLayoutTemplate,
dependencies: [Icon, Button],
})

/**
Expand Down Expand Up @@ -476,6 +482,9 @@ class FlexibleColumnLayout extends UI5Element {
}

onSeparatorPress(e: TouchEvent | MouseEvent) {
if (e.target as HTMLElement === this.startArrowDOM) {
return;
}
const pressedSeparator = (e.target as HTMLElement).closest(".ui5-fcl-separator") as HTMLElement;
if (pressedSeparator.classList.contains("ui5-fcl-separator-start") && !this.showStartSeparatorGrip) {
return;
Expand Down Expand Up @@ -654,11 +663,26 @@ class FlexibleColumnLayout extends UI5Element {
return columnLayoutToAdjust;
}

async _onkeydown(e: KeyboardEvent) {
_onArrowKeydown(e: KeyboardEvent) {
if (isEnter(e) || isSpace(e)) {
e.preventDefault();
const focusedElement = e.target as HTMLElement;
if (focusedElement === this.startArrowDOM) {
this.switchLayoutOnArrowPress();
}
}
}

async _onSeparatorKeydown(e: KeyboardEvent) {
const separator = e.target as HTMLElement;
if (!separator.classList.contains("ui5-fcl-separator")) {
return;
}
const stepSize = 2,
bigStepSize = this._width,
isRTL = this.effectiveDir === "rtl";
let step = 0;

if (isLeft(e)) {
step = -stepSize * 10;
} else if (isRight(e)) {
Expand All @@ -679,7 +703,6 @@ class FlexibleColumnLayout extends UI5Element {
return;
}

const separator = e.target as HTMLElement;
if (!this.separatorMovementSession) {
this.separatorMovementSession = this.initSeparatorMovementSession(separator, 0, false);
}
Expand All @@ -693,7 +716,7 @@ class FlexibleColumnLayout extends UI5Element {
separator.focus();
}

_onkeyup() {
_onSeparatorKeyUp() {
if (this.separatorMovementSession) {
this.onSeparatorMoveEnd();
}
Expand Down Expand Up @@ -818,6 +841,30 @@ class FlexibleColumnLayout extends UI5Element {
return FCLLayout.ThreeColumnsMidExpanded;
}

if (moved({
separator: "start",
from: FCLLayout.ThreeColumnsStartHiddenMidExpanded,
forward: true,
}) && !isTablet && Math.ceil(startColumnPxWidth) >= COLUMN_MIN_WIDTH) {
return FCLLayout.ThreeColumnsMidExpanded;
}

if (moved({
separator: "end",
from: FCLLayout.ThreeColumnsStartHiddenMidExpanded,
forward: false,
}) && newColumnWidths.mid < newColumnWidths.end) {
return FCLLayout.ThreeColumnsStartHiddenEndExpanded;
}

if (moved({
separator: "end",
from: FCLLayout.ThreeColumnsStartHiddenEndExpanded,
forward: true,
}) && newColumnWidths.mid >= newColumnWidths.end) {
return FCLLayout.ThreeColumnsStartHiddenMidExpanded;
}

if (moved({
separator: "end",
from: FCLLayout.ThreeColumnsMidExpandedEndHidden,
Expand Down Expand Up @@ -880,6 +927,14 @@ class FlexibleColumnLayout extends UI5Element {
return fclLayoutBeforeMove; // no layout change
}

switchLayoutOnArrowPress() {
const lastUsedLayout = this.layout as FCLLayout;
this.layout = getNextLayoutByArrowPress()[lastUsedLayout as keyof typeof getNextLayoutByArrowPress];
if (this.layout !== lastUsedLayout) {
this.fireLayoutChange(true, false);
}
}

get _availableWidthForColumns() {
let width = this._width;
if (this.showStartSeparator) {
Expand Down Expand Up @@ -982,6 +1037,10 @@ class FlexibleColumnLayout extends UI5Element {
return this.disableResizing ? false : this.startSeparatorGripVisibility;
}

get showStartSeparatorArrow() {
return this.disableResizing ? false : this.startSeparatorArrowVisibility;
}

get showEndSeparatorGrip() {
return this.disableResizing ? false : this.endSeparatorGripVisibility;
}
Expand All @@ -994,6 +1053,18 @@ class FlexibleColumnLayout extends UI5Element {
return this.effectiveSeparatorsInfo[1].gripVisible;
}

get startSeparatorArrowVisibility() {
return this.effectiveSeparatorsInfo[0].arrowVisible;
}

get startArrowDirection() {
return this.effectiveSeparatorsInfo[0].arrowDirection;
}

get startArrowDOM() {
return this.shadowRoot!.querySelector<HTMLElement>(".ui5-fcl-arrow--start")!;
}

get effectiveSeparatorsInfo() {
return this._effectiveLayoutsByMedia[this.media][this.effectiveLayout].separators;
}
Expand Down
22 changes: 18 additions & 4 deletions packages/fiori/src/FlexibleColumnLayoutTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import AnimationMode from "@ui5/webcomponents-base/dist/types/AnimationMode.js";
import { getAnimationMode } from "@ui5/webcomponents-base/dist/InitialConfiguration.js";
import verticalGrip from "@ui5/webcomponents-icons/dist/vertical-grip.js";
import Icon from "@ui5/webcomponents/dist/Icon.js";
import Button from "@ui5/webcomponents/dist/Button.js";
import type FlexibleColumnLayout from "./FlexibleColumnLayout.js";

export default function FlexibleColumnLayoutTemplate(this: FlexibleColumnLayout) {
Expand Down Expand Up @@ -31,9 +32,10 @@ export default function FlexibleColumnLayoutTemplate(this: FlexibleColumnLayout)
tabindex={this.startSeparatorTabIndex}
onMouseDown={this.onSeparatorPress}
onTouchStart={this.onSeparatorPress}
onKeyDown={this._onkeydown}
onKeyUp={this._onkeyup}
onKeyDown={this._onSeparatorKeydown}
onKeyUp={this._onSeparatorKeyUp}
>
{ this.showStartSeparatorArrow && arrowStart.call(this) }
{ gripStart.call(this) }
</div>

Expand All @@ -59,8 +61,8 @@ export default function FlexibleColumnLayoutTemplate(this: FlexibleColumnLayout)
tabindex={this.endSeparatorTabIndex}
onMouseDown={this.onSeparatorPress}
onTouchStart={this.onSeparatorPress}
onKeyDown={this._onkeydown}
onKeyUp={this._onkeyup}
onKeyDown={this._onSeparatorKeydown}
onKeyUp={this._onSeparatorKeyUp}
>
{ gripEnd.call(this) }
</div>
Expand All @@ -82,6 +84,18 @@ export default function FlexibleColumnLayoutTemplate(this: FlexibleColumnLayout)
);
}

function arrowStart(this: FlexibleColumnLayout) {
return (
<Button
icon={this.startArrowDirection === "backward" ? "slim-arrow-left" : "slim-arrow-right"}
design="Transparent"
onClick={this.switchLayoutOnArrowPress}
onKeyDown={this._onArrowKeydown}
class="ui5-fcl-arrow ui5-fcl-arrow--start"
/>
);
}

function gripStart(this: FlexibleColumnLayout) {
return (
<Icon
Expand Down
78 changes: 76 additions & 2 deletions packages/fiori/src/fcl-utils/FCLLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ type LayoutConfiguration = {
separators: Array<{
visible: boolean;
gripVisible?: boolean;
arrowVisible?: boolean;
arrowDirection?: "forward" | "backward";
}>;
};
};
Expand Down Expand Up @@ -40,14 +42,24 @@ const getLayoutsByMedia = (): LayoutConfiguration => {
"ThreeColumnsMidExpanded": {
layout: ["25%", "50%", "25%"],
separators: [
{ visible: true, gripVisible: true },
{
visible: true,
gripVisible: true,
arrowVisible: true,
arrowDirection: "backward",
},
{ visible: true, gripVisible: true },
],
},
"ThreeColumnsEndExpanded": {
layout: ["25%", "25%", "50%"],
separators: [
{ visible: true, gripVisible: false },
{
visible: true,
gripVisible: false,
arrowVisible: true,
arrowDirection: "backward",
},
{ visible: true, gripVisible: true },
],
},
Expand All @@ -65,6 +77,30 @@ const getLayoutsByMedia = (): LayoutConfiguration => {
{ visible: true, gripVisible: true },
],
},
"ThreeColumnsStartHiddenMidExpanded": {
layout: ["0px", "33%", "67%"],
separators: [
{
visible: true,
gripVisible: true,
arrowVisible: true,
arrowDirection: "forward",
},
{ visible: true, gripVisible: true },
],
},
"ThreeColumnsStartHiddenEndExpanded": {
layout: ["0px", "33%", "67%"],
separators: [
{
visible: true,
gripVisible: false,
arrowVisible: true,
arrowDirection: "forward",
},
{ visible: true, gripVisible: true },
],
},
"MidColumnFullScreen": {
layout: ["0px", "100%", "0px"],
separators: [
Expand Down Expand Up @@ -130,6 +166,20 @@ const getLayoutsByMedia = (): LayoutConfiguration => {
{ visible: true, gripVisible: true },
],
},
"ThreeColumnsStartHiddenMidExpanded": {
layout: ["0px", "67%", "33%"],
separators: [
{ visible: true, gripVisible: true },
{ visible: true, gripVisible: true },
],
},
"ThreeColumnsStartHiddenEndExpanded": {
layout: ["0px", "33%", "67%"],
separators: [
{ visible: false },
{ visible: true, gripVisible: true },
],
},
"MidColumnFullScreen": {
layout: ["0px", "100%", "0px"],
separators: [
Expand Down Expand Up @@ -195,6 +245,20 @@ const getLayoutsByMedia = (): LayoutConfiguration => {
{ visible: false },
],
},
"ThreeColumnsStartHiddenMidExpanded": {
layout: ["0px", "0px", "100%"],
separators: [
{ visible: false },
{ visible: false },
],
},
"ThreeColumnsStartHiddenEndExpanded": {
layout: ["0px", "0px", "100%"],
separators: [
{ visible: false },
{ visible: false },
],
},
"MidColumnFullScreen": {
layout: ["0px", "100%", "0px"],
separators: [
Expand All @@ -213,8 +277,18 @@ const getLayoutsByMedia = (): LayoutConfiguration => {
};
};

const getNextLayoutByArrowPress = () => {
return {
"ThreeColumnsMidExpanded": "ThreeColumnsStartHiddenMidExpanded",
"ThreeColumnsEndExpanded": "ThreeColumnsStartHiddenEndExpanded",
"ThreeColumnsStartHiddenMidExpanded": "ThreeColumnsMidExpanded",
"ThreeColumnsStartHiddenEndExpanded": "ThreeColumnsEndExpanded",
};
};

export {
getLayoutsByMedia,
getNextLayoutByArrowPress,
};

export type {
Expand Down
21 changes: 19 additions & 2 deletions packages/fiori/src/themes/FlexibleColumnLayout.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
display: none;
}

/* arrow */
/* separator */
.ui5-fcl-separator {
display: flex;
align-items: center;
Expand All @@ -59,7 +59,19 @@
padding-bottom: 0.3125rem;
}

/* arrow decoration */
.ui5-fcl-arrow {
position: absolute;
top: 1rem;
width: 1.5rem;
height: 1.5rem;
min-width: 1.5rem;
will-change: transform;
overflow: visible;
z-index: 10;
cursor: pointer;
}

/* grip decoration */
.ui5-fcl-grip:before {
background-image: var(--_ui5_fcl_decoration_top);
bottom: calc(50% + 1rem);
Expand Down Expand Up @@ -88,6 +100,11 @@
height: calc(50% - 1rem);
}

/* Only apply shorter height when arrow is visible */
.ui5-fcl-separator:hover .ui5-fcl-arrow + .ui5-fcl-grip::before {
height: calc(40% - 1rem);
}

[aria-hidden] ::slotted([slot="startColumn"]),
[aria-hidden] ::slotted([slot="midColumn"]),
[aria-hidden] ::slotted([slot="endColumn"]){
Expand Down
Loading

0 comments on commit 61b94ae

Please sign in to comment.