Skip to content

Commit 5dac57c

Browse files
authored
Rework toolbar classnames (tldraw#6526)
For workflow builder, we want a vertical toolbar. I'm backporting it to the SDK because it's a common enough request (tldraw#6532), but in the process i got super confused by two things: * We have a `tlui-toolbar` classname and a `TldrawUiToolbar` component. These are completely unrelated. * We have some `tlui-buttons` classnames that are _almost always_ used with `TldrawUiToolbar`, and sometimes contain buttons as the name would suggest. So this diff tweaks those. `tlui-toolbar` (used for the main actual toolbar at the bottom of the screen) and co are now `tlui-main-toolbar`. `tlui-buttons` has been replaced with some components that have the same effect: `TldrawUiRow` and `TldrawUiGrid`. These are now used by `TldrawUiToolbar` depending on the `orientation` prop it gets passed. I wanted to use components instead of classnames for these because if you're customizing our UI by copy-pasting chunks of it, random CSS classnames are even less well documented than components like these. I also wanted names that reflected the way these were used a bit better (ie often not with buttons). ### Change type - [x] `api` ### Release notes ### API Changes - Added `TldrawUiRow` and `TldrawUiGrid` layout classes. - The `tlui-buttons__horizontal` and `tlui-buttons__grid` class names have been replaced by `tlui-row` and `tlui-grid` - The `tlui-toolbar` classnames have been renamed to `tlui-main-toolbar`
1 parent a03de71 commit 5dac57c

24 files changed

Lines changed: 244 additions & 203 deletions

apps/docs/utils/TldrawApiModel.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,21 @@ export class TldrawApiModel extends ApiModel {
131131
return this.resolveToken(component, tokens[2])
132132
}
133133

134+
if (
135+
tokens.length === 9 &&
136+
tokens[0].text === 'import("react").' &&
137+
tokens[1].text === 'ForwardRefExoticComponent' &&
138+
tokens[2].text === '<' &&
139+
tokens[3].kind === ExcerptTokenKind.Reference &&
140+
tokens[4].text === ' & import("react").' &&
141+
tokens[5].text === 'RefAttributes' &&
142+
tokens[6].text === '<' &&
143+
tokens[7].kind === ExcerptTokenKind.Reference &&
144+
tokens[8].text === '>>'
145+
) {
146+
return this.resolveToken(component, tokens[3])
147+
}
148+
134149
if (component.variableTypeExcerpt.text === 'import("react").NamedExoticComponent<object>') {
135150
// this is a `memo` component with no props
136151
return null

apps/examples/src/examples/rich-text-font-extensions/RichTextFontExtension.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.tlui-buttons__horizontal select {
1+
.rich-text-font-extension-select {
22
border: 0;
33
background: transparent;
44
margin: 0 8px;

apps/examples/src/examples/rich-text-font-extensions/RichTextFontExtension.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const components: TLComponents = {
7272
return (
7373
<DefaultRichTextToolbar>
7474
<select
75+
className="rich-text-font-extension-select"
7576
value={currentFontFamily}
7677
onPointerDown={stopEventPropagation}
7778
onChange={(e) => {
@@ -85,6 +86,7 @@ const components: TLComponents = {
8586
))}
8687
</select>
8788
<select
89+
className="rich-text-font-extension-select"
8890
value={currentFontSize}
8991
onPointerDown={stopEventPropagation}
9092
onChange={(e) => {

packages/tldraw/api-report.api.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import { CSSProperties } from 'react';
1919
import { Editor } from '@tldraw/editor';
2020
import { Extension } from '@tiptap/core';
2121
import { Extensions } from '@tiptap/core';
22+
import { ForwardRefExoticComponent } from 'react';
2223
import { Geometry2d } from '@tldraw/editor';
2324
import { Geometry2dFilters } from '@tldraw/editor';
2425
import { Geometry2dOptions } from '@tldraw/editor';
2526
import { Group2d } from '@tldraw/editor';
2627
import { HandleSnapGeometry } from '@tldraw/editor';
28+
import { HTMLAttributes } from 'react';
2729
import { IndexKey } from '@tldraw/editor';
2830
import { JsonObject } from '@tldraw/editor';
2931
import { JSX as JSX_2 } from 'react/jsx-runtime';
@@ -43,6 +45,7 @@ import { ReadonlySharedStyleMap } from '@tldraw/editor';
4345
import { RecordProps } from '@tldraw/editor';
4446
import { Rectangle2d } from '@tldraw/editor';
4547
import { RecursivePartial } from '@tldraw/editor';
48+
import { RefAttributes } from 'react';
4649
import { RefObject } from 'react';
4750
import { Result } from '@tldraw/editor';
4851
import { RichTextFontVisitorState } from '@tldraw/editor';
@@ -3027,6 +3030,9 @@ export function TldrawUiDropdownMenuTrigger({ children, ...rest }: TLUiDropdownM
30273030
// @public (undocumented)
30283031
export function TldrawUiEventsProvider({ onEvent, children }: EventsProviderProps): JSX_2.Element;
30293032

3033+
// @public
3034+
export const TldrawUiGrid: ForwardRefExoticComponent<TLUiLayoutProps & RefAttributes<HTMLDivElement>>;
3035+
30303036
// @public (undocumented)
30313037
export const TldrawUiIcon: NamedExoticComponent<TLUiIconProps>;
30323038

@@ -3061,7 +3067,7 @@ export function TldrawUiMenuSubmenu<Translation extends string = string>({ id, d
30613067
export function TldrawUiMenuToolItem({ toolId, ...rest }: TLUiMenuToolItemProps): JSX_2.Element | null;
30623068

30633069
// @public (undocumented)
3064-
export function TldrawUiPopover({ id, children, onOpenChange, open }: TLUiPopoverProps): JSX_2.Element;
3070+
export function TldrawUiPopover({ id, children, onOpenChange, open, className }: TLUiPopoverProps): JSX_2.Element;
30653071

30663072
// @public (undocumented)
30673073
export function TldrawUiPopoverContent({ side, children, align, sideOffset, alignOffset, disableEscapeKeyDown, autoFocusFirstButton, }: TLUiPopoverContentProps): JSX_2.Element;
@@ -3078,6 +3084,9 @@ export interface TldrawUiProps extends TLUiContextProviderProps {
30783084
renderDebugMenuItems?(): React_3.ReactNode;
30793085
}
30803086

3087+
// @public
3088+
export const TldrawUiRow: ForwardRefExoticComponent<TLUiLayoutProps & RefAttributes<HTMLDivElement>>;
3089+
30813090
// @public (undocumented)
30823091
export const TldrawUiSlider: React_3.ForwardRefExoticComponent<TLUiSliderProps & React_3.RefAttributes<HTMLDivElement>>;
30833092

@@ -3970,6 +3979,14 @@ export type TLUiKeyboardShortcutsDialogProps = TLUiDialogProps & {
39703979
children?: ReactNode;
39713980
};
39723981

3982+
// @public (undocumented)
3983+
export interface TLUiLayoutProps extends HTMLAttributes<HTMLDivElement> {
3984+
// (undocumented)
3985+
asChild?: boolean;
3986+
// (undocumented)
3987+
children: ReactNode;
3988+
}
3989+
39733990
// @public (undocumented)
39743991
export interface TLUiMainMenuProps {
39753992
// (undocumented)
@@ -4113,6 +4130,8 @@ export interface TLUiPopoverProps {
41134130
// (undocumented)
41144131
children: React_3.ReactNode;
41154132
// (undocumented)
4133+
className?: string;
4134+
// (undocumented)
41164135
id: string;
41174136
// (undocumented)
41184137
onOpenChange?(isOpen: boolean): void;
@@ -4256,6 +4275,8 @@ export interface TLUiToolbarProps extends React_3.HTMLAttributes<HTMLDivElement>
42564275
dir?: 'ltr' | 'rtl';
42574276
// (undocumented)
42584277
label: string;
4278+
// (undocumented)
4279+
orientation?: 'grid' | 'horizontal';
42594280
}
42604281

42614282
// @public (undocumented)

packages/tldraw/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ export { DefaultA11yAnnouncer, useSelectedShapesAnnouncer } from './lib/ui/compo
2525
export { AccessibilityMenu } from './lib/ui/components/AccessibilityMenu'
2626
export { ColorSchemeMenu } from './lib/ui/components/ColorSchemeMenu'
2727
export { DefaultDialogs } from './lib/ui/components/Dialogs'
28+
export {
29+
TldrawUiGrid,
30+
TldrawUiRow,
31+
type TLUiLayoutProps,
32+
} from './lib/ui/components/primitives/layout'
2833
export {
2934
TldrawUiMenuActionCheckboxItem,
3035
type TLUiMenuActionCheckboxItemProps,

packages/tldraw/src/lib/ui.css

Lines changed: 25 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,6 @@
161161
}
162162
}
163163

164-
/* Panel button */
165-
166-
.tlui-button__panel {
167-
position: relative;
168-
}
169-
170164
/* Menu button */
171165

172166
.tlui-button__menu {
@@ -207,7 +201,7 @@
207201

208202
/* Tool lock button */
209203

210-
.tlui-toolbar__lock-button {
204+
.tlui-main-toolbar__lock-button {
211205
position: absolute;
212206
top: 4px;
213207
right: 0px;
@@ -218,7 +212,7 @@
218212
border-radius: var(--radius-2);
219213
}
220214

221-
.tlui-toolbar__lock-button::after {
215+
.tlui-main-toolbar__lock-button::after {
222216
top: 4px;
223217
left: 8px;
224218
inset: 4px;
@@ -230,16 +224,6 @@
230224
position: relative;
231225
height: 48px;
232226
width: 48px;
233-
margin-left: -2px;
234-
margin-right: -2px;
235-
}
236-
237-
.tlui-button__tool:nth-of-type(1) {
238-
margin-left: 0px;
239-
}
240-
241-
.tlui-button__tool:nth-last-of-type(1) {
242-
margin-right: 0px;
243227
}
244228

245229
.tlui-button__tool::after {
@@ -270,47 +254,30 @@
270254
width: 16px;
271255
}
272256

273-
/* Button Row */
257+
/* Row layout */
274258

275-
.tlui-buttons__horizontal {
259+
.tlui-row {
276260
display: flex;
277261
flex-direction: row;
262+
padding: 0 2px;
278263
}
279-
.tlui-buttons__horizontal > * {
264+
.tlui-row > * {
280265
margin-left: -2px;
281266
margin-right: -2px;
282267
}
283-
.tlui-buttons__horizontal > *:nth-child(1) {
284-
margin-left: 0px;
285-
}
286-
.tlui-buttons__horizontal > *:nth-last-child(1) {
287-
margin-right: 0px;
288-
}
289268

290-
/* Button Grid */
269+
/* Grid layout */
291270

292-
.tlui-buttons__grid {
271+
.tlui-grid {
293272
display: grid;
294-
grid-template-columns: repeat(4, auto);
273+
grid-template-columns: repeat(4, 1fr);
295274
grid-auto-flow: row;
296275
overflow: hidden;
276+
padding: 2px;
297277
}
298-
.tlui-buttons__grid > .tlui-button {
278+
.tlui-grid > * {
299279
margin: -2px;
300280
}
301-
.tlui-buttons__grid > .tlui-button:nth-of-type(4n),
302-
.tlui-buttons__vertical-align > .tlui-button:nth-of-type(3n) {
303-
margin-right: 0px;
304-
}
305-
.tlui-buttons__grid > .tlui-button:nth-of-type(4n - 3) {
306-
margin-left: 0px;
307-
}
308-
.tlui-buttons__grid > .tlui-button:nth-of-type(-n + 4) {
309-
margin-top: 0px;
310-
}
311-
.tlui-buttons__grid > .tlui-button:nth-last-of-type(-n + 4) {
312-
margin-bottom: 0px;
313-
}
314281

315282
/* Zoom button */
316283

@@ -1033,12 +1000,8 @@
10331000
border-bottom: 1px solid var(--color-divider);
10341001
}
10351002

1036-
.tlui-style-panel__row {
1037-
display: flex;
1038-
}
1039-
/* Only really used for the alignment picker */
1040-
.tlui-style-panel__row__extra-button {
1041-
margin-left: -2px;
1003+
.tlui-style-panel__dropdown-picker:only-child {
1004+
width: 100%;
10421005
}
10431006

10441007
.tlui-style-panel__double-select-picker {
@@ -1154,7 +1117,7 @@
11541117
/* --------------------- Toolbar -------------------- */
11551118

11561119
/* Wide container */
1157-
.tlui-toolbar {
1120+
.tlui-main-toolbar {
11581121
grid-column: 1 / span 3;
11591122
grid-row: 1;
11601123
display: flex;
@@ -1165,20 +1128,20 @@
11651128
}
11661129

11671130
/* Centered Content */
1168-
.tlui-toolbar__inner {
1131+
.tlui-main-toolbar__inner {
11691132
position: relative;
11701133
width: fit-content;
11711134
display: flex;
11721135
gap: var(--space-3);
11731136
align-items: flex-end;
11741137
}
11751138

1176-
.tlui-toolbar__left {
1139+
.tlui-main-toolbar__left {
11771140
width: fit-content;
11781141
}
11791142

11801143
/* Row of controls + lock button */
1181-
.tlui-toolbar__extras {
1144+
.tlui-main-toolbar__extras {
11821145
position: relative;
11831146
z-index: var(--layer-above);
11841147
width: 100%;
@@ -1187,11 +1150,11 @@
11871150
height: 48px;
11881151
}
11891152

1190-
.tlui-toolbar__extras:empty {
1153+
.tlui-main-toolbar__extras:empty {
11911154
display: none;
11921155
}
11931156

1194-
.tlui-toolbar__extras__controls {
1157+
.tlui-main-toolbar__extras__controls {
11951158
display: flex;
11961159
position: relative;
11971160
flex-direction: row;
@@ -1206,7 +1169,7 @@
12061169
width: fit-content;
12071170
}
12081171

1209-
.tlui-toolbar__tools {
1172+
.tlui-main-toolbar__tools {
12101173
display: flex;
12111174
flex-direction: row;
12121175
align-items: center;
@@ -1217,37 +1180,29 @@
12171180
background: var(--color-panel);
12181181
box-shadow: var(--shadow-2);
12191182
}
1220-
.tlui-toolbar__tools__list {
1221-
display: flex;
1222-
flex-direction: row;
1223-
align-items: center;
1224-
}
12251183

1226-
.tlui-toolbar__overflow {
1184+
.tlui-main-toolbar__overflow {
12271185
width: 40px;
1186+
margin-left: 2px;
12281187
}
12291188

1230-
.tlui-layout__mobile .tlui-toolbar__overflow {
1189+
.tlui-layout__mobile .tlui-main-toolbar__overflow {
12311190
width: 32px;
12321191
padding: 0px;
12331192
}
12341193

1235-
.tlui-toolbar *[data-state='open']::after {
1194+
.tlui-main-toolbar *[data-state='open']::after {
12361195
background: linear-gradient(0deg, rgba(144, 144, 144, 0) 0%, var(--color-muted-2) 100%);
12371196
opacity: 1;
12381197
}
12391198

12401199
@media (hover: hover) {
1241-
.tlui-toolbar *[data-state='open']:not(:hover)::after {
1200+
.tlui-main-toolbar *[data-state='open']:not(:hover)::after {
12421201
background: linear-gradient(0deg, rgba(144, 144, 144, 0) 0%, var(--color-muted-2) 100%);
12431202
opacity: 1;
12441203
}
12451204
}
12461205

1247-
.tlui-layout__mobile .tlui-toolbar {
1248-
transition: transform 0.15s ease-out 0.05s;
1249-
}
1250-
12511206
/* ------------------- Tooltip -------------------- */
12521207

12531208
.tlui-tooltip {

packages/tldraw/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ export const DefaultActionsMenu = memo(function DefaultActionsMenu({
6262
<TldrawUiToolbar
6363
ref={ref}
6464
label={msg('actions-menu.title')}
65-
className="tlui-actions-menu tlui-buttons__grid"
65+
className="tlui-actions-menu"
6666
data-testid="actions-menu.content"
67+
orientation="grid"
6768
>
6869
<TldrawUiMenuContextProvider type="icons" sourceId="actions-menu">
6970
{content}

packages/tldraw/src/lib/ui/components/DefaultMenuPanel.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useBreakpoint } from '../context/breakpoints'
55
import { useTldrawUiComponents } from '../context/components'
66
import { useTranslation } from '../hooks/useTranslation/useTranslation'
77
import { TldrawUiToolbar } from './primitives/TldrawUiToolbar'
8+
import { TldrawUiRow } from './primitives/layout'
89

910
/** @public @react */
1011
export const DefaultMenuPanel = memo(function MenuPanel() {
@@ -32,16 +33,16 @@ export const DefaultMenuPanel = memo(function MenuPanel() {
3233

3334
return (
3435
<nav ref={ref} className="tlui-menu-zone">
35-
<div className="tlui-buttons__horizontal">
36+
<TldrawUiRow>
3637
{MainMenu && <MainMenu />}
3738
{PageMenu && !isSinglePageMode && <PageMenu />}
3839
{showQuickActions ? (
39-
<TldrawUiToolbar className="tlui-buttons__horizontal" label={msg('actions-menu.title')}>
40+
<TldrawUiToolbar orientation="horizontal" label={msg('actions-menu.title')}>
4041
{QuickActions && <QuickActions />}
4142
{ActionsMenu && <ActionsMenu />}
4243
</TldrawUiToolbar>
4344
) : null}
44-
</div>
45+
</TldrawUiRow>
4546
</nav>
4647
)
4748
})

0 commit comments

Comments
 (0)