Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/node-subtitle-descenders-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflowbuilder/ui': patch
---

The `NodeDescription` subtitle no longer crops the bottom of letters such as g, p and y.
5 changes: 5 additions & 0 deletions .changeset/node-subtitle-descenders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflowbuilder/sdk': patch
---

The node subtitle no longer crops the bottom of letters such as g, p and y.
5 changes: 5 additions & 0 deletions .changeset/properties-header-node.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflowbuilder/sdk': minor
---

The Properties panel header shows the selected node's icon in its accent, its label and its type label, or the selected edge's icon, label and "Link", with the collapse toggle on the right. `PropertiesBarProps.headerLabel` is now shown only while nothing is selected, and always names the panel region for screen readers.
1 change: 1 addition & 0 deletions packages/sdk/src/features/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const en = {
},
propertiesBar: {
label: 'Properties',
edge: 'Link',
deleteNode: 'Delete node',
deleteEdge: 'Delete edge',
},
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/features/i18n/locales/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const pl = {
},
propertiesBar: {
label: 'Właściwości',
edge: 'Połączenie',
deleteNode: 'Usuń węzeł',
deleteEdge: 'Usuń krawędź',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.container {
display: flex;
align-items: center;
gap: var(--wb-ds-space-100);

.text {
display: flex;
flex-direction: column;
gap: var(--wb-ds-space-25);
min-width: 0;
}

.label,
.subtitle {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.label {
color: var(--wb-ds-ui-text-default);
}

.subtitle {
/* The 100% line height of wb-text-label-s crops descenders once overflow is hidden. */
line-height: 1.4;
color: var(--wb-ds-ui-text-subtle-default);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { NodeIcon, type NodeIconAccent } from '@workflowbuilder/ui';
import clsx from 'clsx';

import { Icon } from '@workflow-builder/icons';

import styles from './node-heading.module.css';

import type { IconType } from '../../../../node/common';

type Props = {
label: string;
/** Second line under the label, for example the node type. */
subtitle?: string;
icon?: IconType;
accent?: NodeIconAccent;
};

export function NodeHeading({ label, subtitle, icon, accent }: Props) {
return (
<div className={styles['container']}>
{icon && <NodeIcon icon={<Icon name={icon} size="inherit" />} accent={accent} />}
<div className={styles['text']}>
{/* Native title until the DS Tooltip takes over; no keyboard or touch access today (follow-up: node-text-ds-tooltip). */}
<span className={clsx('wb-text-title-s-emphasized', styles['label'])} title={label}>
{label}
</span>
{subtitle && (
<span className={clsx('wb-text-label-s', styles['subtitle'])} title={subtitle}>
{subtitle}
</span>
)}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
.header {
--wb-public-node-icon-padding: var(--wb-ds-space-125);

display: flex;
justify-content: space-between;
align-items: center;
gap: 0.5rem;

.text-container {
display: flex;
flex-direction: column;
flex-grow: 1;
gap: var(--wb-ds-space-100);
/* The collapsed panel sizes to its content, so a long label would widen it past the open panel.
2px is the panel's 1px border on each side. */
max-width: calc(var(--wb-sdk-sidebar-expanded-width) - 2 * var(--wb-sdk-sidebar-horizontal-padding) - 2px);

p {
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
margin: 0;
}
.heading {
flex: 1;
min-width: 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,28 @@ import { Icon } from '@workflow-builder/icons';

import styles from './properties-bar-header.module.css';

import { useTranslateIfPossible } from '../../../../hooks/use-translate-if-possible';
import type { WorkflowBuilderNode } from '../../../../node/node-data';
import { useStore } from '../../../../store/store';
import type { SingleSelectedElement } from '../../use-single-selected-element';
import { NodeHeading } from './node-heading';

type Props = {
header: string;
name: string;
hasSelection: boolean;
isExpendable: boolean;
selection: SingleSelectedElement | null;
headerLabel: string;
isOpen: boolean;
onTogglePropertiesBar: () => void;
onDotsClick?: () => void;
};

export function PropertiesBarHeader({
onTogglePropertiesBar,
isExpendable: isPropertiesBarOpen,
header,
hasSelection,
name,
onDotsClick,
}: Props) {
export function PropertiesBarHeader({ selection, headerLabel, isOpen, onTogglePropertiesBar, onDotsClick }: Props) {
const { t } = useTranslation();
const toggleLabel = isOpen ? t('tooltips.closePropertiesBar') : t('tooltips.openPropertiesBar');

return (
<div className={styles['header']}>
<NavButton
aria-label={isPropertiesBarOpen ? t('tooltips.closePropertiesBar') : t('tooltips.openPropertiesBar')}
size="s"
onClick={onTogglePropertiesBar}
tooltip={isPropertiesBarOpen ? t('tooltips.closePropertiesBar') : t('tooltips.openPropertiesBar')}
disabled={!hasSelection}
prefixIcon={<Icon name="SidebarSimple" />}
/>
<div className={styles['text-container']}>
<span className={name ? 'wb-text-title-s-emphasized' : 'wb-text-title-m-emphasized'}>{header}</span>
{name && <p className="wb-text-label-s">{name}</p>}
<div className={styles['heading']}>
<SelectionHeading selection={selection} headerLabel={headerLabel} />
</div>
{onDotsClick && (
<NavButton
Expand All @@ -46,6 +36,50 @@ export function PropertiesBarHeader({
prefixIcon={<Icon name="DotsThreeVertical" />}
/>
)}
<NavButton
aria-label={toggleLabel}
size="s"
onClick={onTogglePropertiesBar}
tooltip={toggleLabel}
disabled={!selection}
prefixIcon={<Icon name="SidebarSimple" />}
/>
</div>
);
}

type SelectionHeadingProps = Pick<Props, 'selection' | 'headerLabel'>;

function SelectionHeading({ selection, headerLabel }: SelectionHeadingProps) {
const { t } = useTranslation();

if (selection?.node) {
return <SelectedNodeHeading node={selection.node} />;
}

if (!selection?.edge) {
return <span className="wb-text-title-m-emphasized">{headerLabel}</span>;
}

const edgeType = t('propertiesBar.edge');
const { label, icon } = selection.edge.data ?? {};

return <NodeHeading label={label || edgeType} subtitle={edgeType} icon={icon ?? 'CaretRight'} />;
}

function SelectedNodeHeading({ node }: { node: WorkflowBuilderNode }) {
const definition = useStore((store) => store.getNodeDefinition(node.data.type));
const translateIfPossible = useTranslateIfPossible();

const typeLabel = definition && (translateIfPossible(definition.label) || definition.label);
const label = node.data.properties.label;

return (
<NodeHeading
label={label || typeLabel || node.data.type}
subtitle={label ? typeLabel : undefined}
icon={definition?.icon ?? node.data.icon}
accent={definition?.accent}
/>
);
}
Loading
Loading