Skip to content

Commit 44d412b

Browse files
Init AxoIconButton
1 parent 5260600 commit 44d412b

File tree

8 files changed

+531
-68
lines changed

8 files changed

+531
-68
lines changed

ts/axo/AxoButton.dom.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { SpinnerV2 } from '../components/SpinnerV2.dom.js';
1111

1212
const Namespace = 'AxoButton';
1313

14+
type GenericButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
15+
1416
const baseAxoButtonStyles = tw(
1517
'relative inline-flex max-w-full items-center-safe justify-center-safe rounded-full select-none',
1618
'outline-0 outline-border-focused focused:outline-[2.5px]',
@@ -133,11 +135,6 @@ const AxoButtonSizes = {
133135
sm: tw('min-w-12 px-2 py-1 type-body-small font-medium'),
134136
} as const satisfies Record<string, TailwindStyles>;
135137

136-
type BaseButtonAttrs = Omit<
137-
ButtonHTMLAttributes<HTMLButtonElement>,
138-
'className' | 'style' | 'children'
139-
>;
140-
141138
type AxoButtonVariant = keyof typeof AxoButtonVariants;
142139
type AxoButtonSize = keyof typeof AxoButtonSizes;
143140

@@ -215,16 +212,19 @@ export namespace AxoButton {
215212
full: tw('w-full'),
216213
};
217214

218-
export type RootProps = BaseButtonAttrs &
219-
Readonly<{
220-
variant: AxoButtonVariant;
221-
size: AxoButtonSize;
222-
width?: Width;
223-
symbol?: AxoSymbol.InlineGlyphName;
224-
arrow?: boolean;
225-
experimentalSpinner?: { 'aria-label': string } | null;
226-
children: ReactNode;
227-
}>;
215+
export type RootProps = Readonly<{
216+
variant: AxoButtonVariant;
217+
size: AxoButtonSize;
218+
width?: Width;
219+
symbol?: AxoSymbol.InlineGlyphName;
220+
arrow?: boolean;
221+
experimentalSpinner?: { 'aria-label': string } | null;
222+
disabled?: GenericButtonProps['disabled'];
223+
onClick?: GenericButtonProps['onClick'];
224+
children: ReactNode;
225+
// Note: Technically we forward all props for Radix, but we restrict the
226+
// props that the type accepts
227+
}>;
228228

229229
export const Root: FC<RootProps> = memo(
230230
forwardRef((props, ref: ForwardedRef<HTMLButtonElement>) => {
@@ -251,9 +251,9 @@ export namespace AxoButton {
251251
return (
252252
<button
253253
ref={ref}
254+
{...rest}
254255
type="button"
255256
className={tw(variantStyles, sizeStyles, widthStyles)}
256-
{...rest}
257257
>
258258
<span
259259
className={tw(

ts/axo/AxoDialog.dom.stories.tsx

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ function Template(props: {
4141
back?: boolean;
4242
contentSize: AxoDialog.ContentSize;
4343
bodyPadding?: AxoDialog.BodyPadding;
44+
iconAction?: boolean;
4445
footerContent?: ReactNode;
4546
children: ReactNode;
4647
}): JSX.Element {
@@ -68,12 +69,26 @@ function Template(props: {
6869
</AxoDialog.FooterContent>
6970
)}
7071
<AxoDialog.Actions>
71-
<AxoDialog.Action variant="secondary" onClick={action('onCancel')}>
72-
Cancel
73-
</AxoDialog.Action>
74-
<AxoDialog.Action variant="primary" onClick={action('onSave')}>
75-
Save
76-
</AxoDialog.Action>
72+
{props.iconAction ? (
73+
<AxoDialog.IconAction
74+
aria-label="Send"
75+
variant="primary"
76+
symbol="send-fill"
77+
onClick={action('onSend')}
78+
/>
79+
) : (
80+
<>
81+
<AxoDialog.Action
82+
variant="secondary"
83+
onClick={action('onCancel')}
84+
>
85+
Cancel
86+
</AxoDialog.Action>
87+
<AxoDialog.Action variant="primary" onClick={action('onSave')}>
88+
Save
89+
</AxoDialog.Action>
90+
</>
91+
)}
7792
</AxoDialog.Actions>
7893
</AxoDialog.Footer>
7994
</AxoDialog.Content>
@@ -102,6 +117,14 @@ export function Large(): JSX.Element {
102117
return <Template contentSize="lg">{TEXT_LONG}</Template>;
103118
}
104119

120+
export function IconAction(): JSX.Element {
121+
return (
122+
<Template contentSize="sm" iconAction>
123+
{TEXT_SHORT}
124+
</Template>
125+
);
126+
}
127+
105128
export function LongContent(): JSX.Element {
106129
return (
107130
<Template contentSize="md">
@@ -166,7 +189,8 @@ function TextInputField(props: { placeholder: string }) {
166189
'w-full px-3 py-1.5',
167190
'border-[0.5px] border-border-primary shadow-elevation-0',
168191
'rounded-lg bg-fill-primary',
169-
'placeholder:text-label-placeholder'
192+
'placeholder:text-label-placeholder',
193+
'forced-colors:border forced-colors:border-[ButtonBorder] forced-colors:bg-[ButtonFace] forced-colors:text-[ButtonText]'
170194
)}
171195
/>
172196
</div>
@@ -193,7 +217,10 @@ export function ExampleNicknameAndNoteDialog(): JSX.Element {
193217
encrypted. They are only visible to you.
194218
</p>
195219
<div
196-
className={tw('mx-auto size-20 rounded-full bg-color-fill-primary')}
220+
className={tw(
221+
'mx-auto size-20 rounded-full bg-color-fill-primary',
222+
'forced-colors:border'
223+
)}
197224
/>
198225
<Spacer height={12} />
199226
<TextInputField placeholder="First name" />
@@ -331,7 +358,10 @@ export function ExampleLanguageDialog(): JSX.Element {
331358
// eslint-disable-next-line jsx-a11y/no-autofocus
332359
autoFocus
333360
placeholder="Search languages"
334-
className={tw('w-full rounded-lg bg-fill-secondary px-3 py-[5px]')}
361+
className={tw(
362+
'w-full rounded-lg bg-fill-secondary px-3 py-[5px]',
363+
'forced-colors:border forced-colors:border-[ButtonBorder] forced-colors:bg-[ButtonFace] forced-colors:text-[ButtonText]'
364+
)}
335365
/>
336366
</AxoDialog.ExperimentalSearch>
337367
<AxoDialog.Body padding="only-scrollbar-gutter">

ts/axo/AxoDialog.dom.tsx

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { tw } from './tw.dom.js';
1616
import { AxoScrollArea } from './AxoScrollArea.dom.js';
1717
import { getScrollbarGutters } from './_internal/scrollbars.dom.js';
1818
import { AxoButton } from './AxoButton.dom.js';
19+
import { AxoIconButton } from './AxoIconButton.dom.js';
1920

2021
const Namespace = 'AxoDialog';
2122

@@ -237,9 +238,11 @@ export namespace AxoDialog {
237238
export const Back: FC<BackProps> = memo(props => {
238239
return (
239240
<div className={tw('col-[back-slot] text-start')}>
240-
<HeaderIconButton
241-
label={props['aria-label']}
241+
<AxoIconButton.Root
242+
size="sm"
243+
variant="borderless-secondary"
242244
symbol="chevron-[start]"
245+
aria-label={props['aria-label']}
243246
/>
244247
</div>
245248
);
@@ -260,7 +263,12 @@ export namespace AxoDialog {
260263
return (
261264
<div className={tw('col-[close-slot] text-end')}>
262265
<Dialog.Close asChild>
263-
<HeaderIconButton label={props['aria-label']} symbol="x" />
266+
<AxoIconButton.Root
267+
size="sm"
268+
variant="borderless-secondary"
269+
symbol="x"
270+
aria-label={props['aria-label']}
271+
/>
264272
</Dialog.Close>
265273
</div>
266274
);
@@ -462,4 +470,31 @@ export namespace AxoDialog {
462470
});
463471

464472
Action.displayName = `${Namespace}.Action`;
473+
474+
/**
475+
* Component: <AxoDialog.Actions>
476+
* ------------------------------
477+
*/
478+
479+
export type IconActionVariant = 'primary' | 'destructive' | 'secondary';
480+
481+
export type IconActionProps = Readonly<{
482+
'aria-label': string;
483+
variant: ActionVariant;
484+
symbol: AxoSymbol.IconName;
485+
onClick: () => void;
486+
}>;
487+
488+
export const IconAction: FC<IconActionProps> = memo(props => {
489+
return (
490+
<AxoIconButton.Root
491+
aria-label={props['aria-label']}
492+
variant={props.variant}
493+
size="md"
494+
symbol={props.symbol}
495+
/>
496+
);
497+
});
498+
499+
IconAction.displayName = `${Namespace}.IconAction`;
465500
}

0 commit comments

Comments
 (0)