diff --git a/components/drawer/README.md b/components/drawer/README.md new file mode 100644 index 00000000000..afbea139854 --- /dev/null +++ b/components/drawer/README.md @@ -0,0 +1,3 @@ + + +# Drawer component diff --git a/components/drawer/css/_mixin.scss b/components/drawer/css/_mixin.scss new file mode 100644 index 00000000000..16adad83b5b --- /dev/null +++ b/components/drawer/css/_mixin.scss @@ -0,0 +1,94 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2021 Robbert Broersma + */ + +@import "../../backdrop/css/mixin"; + +@mixin utrecht-drawer { + /* minimum size for backdrop with light-dismiss is 44px x 44px to meet WCAG SC 2.5.5 */ + --_utrecht-drawer-backdrop-min-size: max(var(--utrecht-drawer-backdrop-min-size), 44px); + + background-color: var(--utrecht-drawer-background-color, Canvas); + border-color: var(--utrecht-drawer-border-color, currentColor); + border-width: var(--utrecht-drawer-border-width, 0); + box-sizing: border-box; + color: var(--utrecht-drawer-color, CanvasText); + overflow: auto; + padding-block-end: var(--utrecht-drawer-padding-block-end); + padding-block-start: var(--utrecht-drawer-padding-block-start); + padding-inline-end: var(--utrecht-drawer-padding-inline-end); + padding-inline-start: var(--utrecht-drawer-padding-inline-start); + position: fixed; + z-index: var(--utrecht-drawer-z-index, 1); +} + +@mixin utrecht-drawer--html-dialog { + margin-block-start: 0; +} + +@mixin utrecht-drawer__backdrop { + @include utrecht-backdrop; +} + +@mixin utrecht-drawer--inline { + block-size: 100%; + inset-block-end: 0; + inset-block-start: 0; + max-block-size: 100%; + max-inline-size: min( + var(--utrecht-drawer-max-inline-size, 100%), + calc(100% - var(--_utrecht-drawer-backdrop-min-size, 44px)) + ); + min-inline-size: var(--utrecht-drawer-min-inline-size, calc((1280px / 4) - var(--_utrecht-drawer-backdrop-min-size))); +} + +@mixin utrecht-drawer--inline-start { + @include utrecht-drawer--inline; + + border-end-end-radius: var(--utrecht-drawer-border-radius); + border-inline-start-width: 0; + border-start-end-radius: var(--utrecht-drawer-border-radius); + inset-inline-end: auto; + inset-inline-start: 0; +} + +@mixin utrecht-drawer--inline-end { + @include utrecht-drawer--inline; + + border-end-start-radius: var(--utrecht-drawer-border-radius); + border-inline-end-width: 0; + border-start-start-radius: var(--utrecht-drawer-border-radius); + inset-inline-end: 0; + inset-inline-start: auto; +} + +@mixin utrecht-drawer--block { + block-size: fit-content; + inline-size: 100%; + inset-inline-end: 0; + inset-inline-start: 0; + max-block-size: min(var(--utrecht-drawer-max-block-size), calc(100% - var(--_utrecht-drawer-backdrop-min-size))); + max-inline-size: 100%; + min-block-size: var(--utrecht-drawer-min-block-size, calc((1024px / 4) - var(--_utrecht-drawer-backdrop-min-size))); +} + +@mixin utrecht-drawer--block-start { + @include utrecht-drawer--block; + + border-block-start-width: 0; + border-end-end-radius: var(--utrecht-drawer-border-radius); + border-end-start-radius: var(--utrecht-drawer-border-radius); + inset-block-end: auto; + inset-block-start: 0; +} + +@mixin utrecht-drawer--block-end { + @include utrecht-drawer--block; + + border-block-end-width: 0; + border-start-end-radius: var(--utrecht-drawer-border-radius); + border-start-start-radius: var(--utrecht-drawer-border-radius); + inset-block-end: 0; + inset-block-start: auto; +} diff --git a/components/drawer/css/index.scss b/components/drawer/css/index.scss new file mode 100644 index 00000000000..01503330b89 --- /dev/null +++ b/components/drawer/css/index.scss @@ -0,0 +1,30 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2021 Robbert Broersma + */ + +@import "./mixin"; + +.utrecht-drawer { + @include utrecht-drawer; +} + +.utrecht-drawer::backdrop { + @include utrecht-drawer__backdrop; +} + +.utrecht-drawer--inline-start { + @include utrecht-drawer--inline-start; +} + +.utrecht-drawer--inline-end { + @include utrecht-drawer--inline-end; +} + +.utrecht-drawer--block-start { + @include utrecht-drawer--block-start; +} + +.utrecht-drawer--block-end { + @include utrecht-drawer--block-end; +} diff --git a/components/drawer/tokens.json b/components/drawer/tokens.json new file mode 100644 index 00000000000..a78c67009b7 --- /dev/null +++ b/components/drawer/tokens.json @@ -0,0 +1,128 @@ +{ + "utrecht": { + "drawer": { + "background-color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "border-color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "border-radius": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "border-width": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "max-block-size": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "min-block-size": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "max-inline-size": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "min-inline-size": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "padding-block-end": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "padding-block-start": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "padding-inline-start": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "padding-inline-end": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "z-index": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + }, + "backdrop": { + "min-size": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + } + } + } + } + } + } +} diff --git a/components/index.scss b/components/index.scss index 00f6cc3eb13..320c71b5b8f 100644 --- a/components/index.scss +++ b/components/index.scss @@ -42,6 +42,7 @@ @import "./data-list/css"; @import "./digid-button/css"; @import "./document/css"; +@import "./drawer/css"; @import "./emphasis/css"; @import "./figure/css"; @import "./form/css"; diff --git a/packages/component-library-react/src/Drawer.tsx b/packages/component-library-react/src/Drawer.tsx new file mode 100644 index 00000000000..1a241a99545 --- /dev/null +++ b/packages/component-library-react/src/Drawer.tsx @@ -0,0 +1,60 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2021 Robbert Broersma + */ + +import clsx from 'clsx'; +import { + DialogHTMLAttributes, + ForwardedRef, + forwardRef, + PropsWithChildren, + useEffect, + useImperativeHandle, + useRef, +} from 'react'; + +export type DrawerAlignType = 'block-end' | 'block-start' | 'inline-end' | 'inline-start'; + +export interface DrawerProps extends DialogHTMLAttributes { + align?: string | DrawerAlignType; + modal?: boolean; +} + +export const Drawer = forwardRef( + ( + { align, children, className, modal, ...restProps }: PropsWithChildren, + ref: ForwardedRef, + ) => { + let dialogRef = useRef(null); + + useImperativeHandle(ref, () => dialogRef.current as HTMLDialogElement); + + useEffect(() => { + if (modal && restProps.open && dialogRef?.current) { + // Dialog must not be `open` initially, prevent the following error: + // "Cannot call showModal() on an open non-modal dialog" + dialogRef.current.close(); + dialogRef.current.showModal(); + } + }); + + return ( + + {children} + + ); + }, +); + +Drawer.displayName = 'Drawer'; diff --git a/packages/component-library-react/src/css-module/Drawer.tsx b/packages/component-library-react/src/css-module/Drawer.tsx new file mode 100644 index 00000000000..5d3ac6edea7 --- /dev/null +++ b/packages/component-library-react/src/css-module/Drawer.tsx @@ -0,0 +1,8 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2021 Robbert Broersma + */ + +import '../../../../components/drawer/css/index.scss'; + +export * from '../Drawer'; diff --git a/packages/component-library-react/src/css-module/index.ts b/packages/component-library-react/src/css-module/index.ts index 22d2ac2131a..bd51d68fe15 100644 --- a/packages/component-library-react/src/css-module/index.ts +++ b/packages/component-library-react/src/css-module/index.ts @@ -49,6 +49,8 @@ export type { } from '../DataList'; export { DataList, DataListItem, DataListActions, DataListKey, DataListValue } from './DataList'; export type { EmphasisProps } from '../Emphasis'; +export type { DrawerAlignType, DrawerProps } from './Drawer'; +export { Drawer } from './Drawer'; export { Emphasis } from './Emphasis'; export type { FieldsetProps } from '../Fieldset'; export { Fieldset } from './Fieldset'; diff --git a/packages/component-library-react/src/index.ts b/packages/component-library-react/src/index.ts index 8c97421ee7f..bd8f7584e63 100644 --- a/packages/component-library-react/src/index.ts +++ b/packages/component-library-react/src/index.ts @@ -55,6 +55,8 @@ export type { DataListValueProps, } from './DataList'; export { DataList, DataListItem, DataListActions, DataListKey, DataListValue } from './DataList'; +export type { DrawerAlignType, DrawerProps } from './Drawer'; +export { Drawer } from './Drawer'; export type { EmphasisProps } from './Emphasis'; export { Emphasis } from './Emphasis'; export type { FieldsetProps } from './Fieldset'; diff --git a/packages/storybook-css/src/Drawer.stories.tsx b/packages/storybook-css/src/Drawer.stories.tsx new file mode 100644 index 00000000000..a74a9c09a50 --- /dev/null +++ b/packages/storybook-css/src/Drawer.stories.tsx @@ -0,0 +1,120 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { Drawer, Heading1, Paragraph } from '@utrecht/component-library-react'; +import readme from '@utrecht/components/drawer/README.md?raw'; +import tokensDefinition from '@utrecht/components/drawer/tokens.json'; +import tokens from '@utrecht/design-tokens/dist/index.json'; +import React from 'react'; +import { designTokenStory } from './design-token-story'; + +const meta = { + title: 'CSS Component/Drawer', + id: 'css-drawer', + component: Drawer, + decorators: [(Story) =>
{Story()}
], + args: { + children: '', + open: false, + modal: false, + }, + argTypes: { + align: { + control: { type: 'select' }, + options: ['', 'block-end', 'block-start', 'inline-end', 'inline-start'], + }, + children: { + description: 'Content of the drawer', + }, + open: { + control: { type: 'boolean' }, + }, + modal: { + control: { type: 'boolean' }, + }, + }, + parameters: { + tokensPrefix: 'utrecht-drawer', + status: { + type: 'WORK IN PROGRESS', + }, + tokens, + tokensDefinition, + docs: { + description: { + component: readme, + }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: [ + Lorem ipsum, + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum. + , + ], + open: true, + }, +}; + +export const InlineStart: Story = { + args: { + ...Default.args, + align: 'inline-start', + }, + name: 'inline-start alignment', +}; + +export const InlineEnd: Story = { + args: { + ...Default.args, + align: 'inline-end', + }, + name: 'inline-end alignment', +}; + +export const BlockStart: Story = { + args: { + ...Default.args, + align: 'block-start', + }, + name: 'block-start alignment', +}; + +export const BlockEnd: Story = { + args: { + ...Default.args, + align: 'block-end', + }, + name: 'block-end alignment', +}; + +export const OverflowY: Story = { + args: { + align: 'inline-start', + children: Array(10) + .fill(0) + .map(() => ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. + + )), + open: true, + }, + name: 'Overflow Y', +}; + +export const DesignTokens = designTokenStory(meta); diff --git a/packages/storybook-react/src/stories/Drawer.stories.tsx b/packages/storybook-react/src/stories/Drawer.stories.tsx new file mode 100644 index 00000000000..a8f5ee7614e --- /dev/null +++ b/packages/storybook-react/src/stories/Drawer.stories.tsx @@ -0,0 +1,195 @@ +import { Meta, StoryObj } from '@storybook/react'; +import type { DrawerProps } from '@utrecht/component-library-react/src'; +import { + Button, + ButtonGroup, + Drawer, + Paragraph, + PrimaryActionButton, +} from '@utrecht/component-library-react/src/css-module'; +import readme from '@utrecht/components/drawer/README.md?raw'; +import tokensDefinition from '@utrecht/components/drawer/tokens.json'; +import tokens from '@utrecht/design-tokens/dist/index.json'; +import React, { createRef, PropsWithChildren, useState } from 'react'; +import { designTokenStory } from './util'; + +const meta = { + title: 'React Component/Drawer', + id: 'react-drawer', + component: Drawer, + decorators: [ + (Story) => ( +
+ {Story()} +
+ ), + ], + args: { + children: '', + hidden: false, + open: false, + modal: false, + }, + argTypes: { + align: { + control: { type: 'select' }, + options: ['', 'block-end', 'block-start', 'inline-end', 'inline-start'], + }, + children: { + description: 'Content of the drawer', + }, + hidden: { + control: { type: 'boolean' }, + }, + open: { + control: { type: 'boolean' }, + }, + modal: { + control: { type: 'boolean' }, + }, + }, + parameters: { + tokensPrefix: 'utrecht-drawer', + tokens, + tokensDefinition, + docs: { + description: { + component: readme, + }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; +export const Default: Story = { + args: { + children: [ + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum. + , + + OK + , + ], + open: true, + }, +}; + +export const InlineStart: Story = { + args: { + ...Default.args, + align: 'inline-start', + }, + name: 'inline-start alignment', +}; + +export const InlineEnd: Story = { + args: { + ...Default.args, + align: 'inline-end', + }, + name: 'inline-end alignment', +}; + +export const BlockStart: Story = { + args: { + ...Default.args, + align: 'block-start', + }, + name: 'block-start alignment', +}; + +export const BlockEnd: Story = { + args: { + ...Default.args, + align: 'block-end', + }, + name: 'block-end alignment', +}; + +interface ShowModalProps extends DrawerProps { + buttonLabel: string; + buttonAppearance?: 'primary-action-button' | 'secondary-action-button' | 'subtle-button'; + buttonHint?: string; +} + +export const ShowModal = { + render: ({ + buttonLabel, + buttonAppearance, + buttonHint, + children, + ...restProps + }: PropsWithChildren) => { + const drawer = createRef(); + const [open, setOpen] = useState(false); + const showModal = () => { + if (drawer.current) { + setOpen(true); + drawer.current.showModal(); + } + }; + return ( +
+ + + + + {children} + +
+ ); + }, + args: { + buttonLabel: 'Open menu', + buttonAppearance: 'subtle-button', + children: ( +
+ + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. + +
+ ), + }, + argTypes: { + buttonLabel: { + type: 'text', + }, + buttonAppearance: { + type: 'select', + options: [undefined, 'primary-action-button', 'secondary-action-button', 'subtle-button'], + }, + buttonHint: { + type: 'select', + options: [undefined, 'warning', 'danger'], + }, + type: { + type: 'select', + options: ['info', 'ok', 'warning', 'error'], + }, + }, +}; + +export const DesignTokens = designTokenStory(meta); diff --git a/packages/storybook-web-component/src/Drawer.stories.tsx b/packages/storybook-web-component/src/Drawer.stories.tsx new file mode 100644 index 00000000000..015d5764f07 --- /dev/null +++ b/packages/storybook-web-component/src/Drawer.stories.tsx @@ -0,0 +1,134 @@ +/* @license CC0-1.0 */ + +import type { Meta, StoryObj } from '@storybook/react'; +import readme from '@utrecht/components/drawer/README.md?raw'; +import tokensDefinition from '@utrecht/components/drawer/tokens.json'; +import tokens from '@utrecht/design-tokens/dist/index.json'; +import { UtrechtDrawer, UtrechtHeading1, UtrechtParagraph } from '@utrecht/web-component-library-react'; +import React from 'react'; +import { designTokenStory } from './design-token-story'; + +const meta = { + title: 'Web Component/Drawer', + id: 'web-component-drawer', + component: UtrechtDrawer, + decorators: [ + (Story) => ( +
+ {Story()} +
+ ), + ], + argTypes: { + align: { + control: { type: 'select' }, + options: ['', 'block-end', 'block-start', 'inline-end', 'inline-start'], + }, + children: { + description: 'HTML content of the drawer', + }, + hidden: { + control: { type: 'boolean' }, + }, + open: { + control: { type: 'boolean' }, + }, + modal: { + control: { type: 'boolean' }, + }, + }, + args: { + children: '', + hidden: false, + open: false, + }, + tags: ['autodocs'], + parameters: { + status: { + type: 'WORK IN PROGRESS', + }, + tokensPrefix: 'utrecht-drawer', + tokens, + tokensDefinition, + docs: { + description: { + component: readme, + }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: [ + Lorem ipsum, + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum. + , + ], + open: true, + }, +}; + +export const InlineStart: Story = { + args: { + ...Default.args, + align: 'inline-start', + }, +}; + +export const InlineEnd: Story = { + args: { + ...Default.args, + align: 'inline-end', + }, +}; + +export const BlockStart: Story = { + args: { + ...Default.args, + align: 'block-start', + }, +}; + +export const BlockEnd: Story = { + args: { + ...Default.args, + align: 'block-end', + }, +}; + +export const OverflowBlock: Story = { + args: { + children: [ + Lorem ipsum, + ...Array(42) + .fill(0) + .map(() => ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat + nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit + anim id est laborum. + + )), + ], + open: true, + }, +}; + +export const DesignTokens = designTokenStory(meta); diff --git a/packages/web-component-library-stencil/src/components.d.ts b/packages/web-component-library-stencil/src/components.d.ts index abfe5b0afcf..34b32c0315d 100644 --- a/packages/web-component-library-stencil/src/components.d.ts +++ b/packages/web-component-library-stencil/src/components.d.ts @@ -96,6 +96,12 @@ export namespace Components { } interface UtrechtDocument { } + interface UtrechtDrawer { + "align": string; + "close": () => Promise; + "open": boolean; + "showModal": () => Promise; + } interface UtrechtEherkenningLogo { } interface UtrechtEidasLogo { @@ -904,6 +910,12 @@ declare global { prototype: HTMLUtrechtDocumentElement; new (): HTMLUtrechtDocumentElement; }; + interface HTMLUtrechtDrawerElement extends Components.UtrechtDrawer, HTMLStencilElement { + } + var HTMLUtrechtDrawerElement: { + prototype: HTMLUtrechtDrawerElement; + new (): HTMLUtrechtDrawerElement; + }; interface HTMLUtrechtEherkenningLogoElement extends Components.UtrechtEherkenningLogo, HTMLStencilElement { } var HTMLUtrechtEherkenningLogoElement: { @@ -2501,6 +2513,7 @@ declare global { "utrecht-digid-button": HTMLUtrechtDigidButtonElement; "utrecht-digid-logo": HTMLUtrechtDigidLogoElement; "utrecht-document": HTMLUtrechtDocumentElement; + "utrecht-drawer": HTMLUtrechtDrawerElement; "utrecht-eherkenning-logo": HTMLUtrechtEherkenningLogoElement; "utrecht-eidas-logo": HTMLUtrechtEidasLogoElement; "utrecht-emphasis": HTMLUtrechtEmphasisElement; @@ -2859,6 +2872,10 @@ declare namespace LocalJSX { } interface UtrechtDocument { } + interface UtrechtDrawer { + "align"?: string; + "open"?: boolean; + } interface UtrechtEherkenningLogo { } interface UtrechtEidasLogo { @@ -3528,6 +3545,7 @@ declare namespace LocalJSX { "utrecht-digid-button": UtrechtDigidButton; "utrecht-digid-logo": UtrechtDigidLogo; "utrecht-document": UtrechtDocument; + "utrecht-drawer": UtrechtDrawer; "utrecht-eherkenning-logo": UtrechtEherkenningLogo; "utrecht-eidas-logo": UtrechtEidasLogo; "utrecht-emphasis": UtrechtEmphasis; @@ -3819,6 +3837,7 @@ declare module "@stencil/core" { "utrecht-digid-button": LocalJSX.UtrechtDigidButton & JSXBase.HTMLAttributes; "utrecht-digid-logo": LocalJSX.UtrechtDigidLogo & JSXBase.HTMLAttributes; "utrecht-document": LocalJSX.UtrechtDocument & JSXBase.HTMLAttributes; + "utrecht-drawer": LocalJSX.UtrechtDrawer & JSXBase.HTMLAttributes; "utrecht-eherkenning-logo": LocalJSX.UtrechtEherkenningLogo & JSXBase.HTMLAttributes; "utrecht-eidas-logo": LocalJSX.UtrechtEidasLogo & JSXBase.HTMLAttributes; "utrecht-emphasis": LocalJSX.UtrechtEmphasis & JSXBase.HTMLAttributes; diff --git a/packages/web-component-library-stencil/src/components/drawer.scss b/packages/web-component-library-stencil/src/components/drawer.scss new file mode 100644 index 00000000000..6feb48fa8ef --- /dev/null +++ b/packages/web-component-library-stencil/src/components/drawer.scss @@ -0,0 +1,15 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2020-2022 Gemeente Utrecht + * Copyright (c) 2020-2022 Frameless B.V. + */ + +@import "~@utrecht/components/drawer/css"; + +:host { + display: block; +} + +:host([hidden]) { + display: none !important; +} diff --git a/packages/web-component-library-stencil/src/components/drawer.tsx b/packages/web-component-library-stencil/src/components/drawer.tsx new file mode 100644 index 00000000000..5a43023bb31 --- /dev/null +++ b/packages/web-component-library-stencil/src/components/drawer.tsx @@ -0,0 +1,53 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2020-2022 Gemeente Utrecht + * Copyright (c) 2020-2022 Frameless B.V. + */ + +import { Component, h, Method, Prop } from '@stencil/core'; +import clsx from 'clsx'; + +@Component({ + tag: 'utrecht-drawer', + styleUrl: 'drawer.scss', + shadow: true, +}) +export class Drawer { + @Prop() align: string; + @Prop() open: boolean = false; + + dialogElement!: HTMLDialogElement; + + render() { + const { align, open } = this; + return ( + (this.dialogElement = el as HTMLDialogElement)} + class={clsx('utrecht-drawer', { + 'utrecht-drawer--block-end': align === 'block-end', + 'utrecht-drawer--block-start': align === 'block-start', + 'utrecht-drawer--inline-end': align === 'inline-end' || !align, + 'utrecht-drawer--inline-start': align === 'inline-start', + })} + open={open} + > + + + ); + } + + @Method() + async close() { + this.dialogElement?.close(); + } + + @Method() + async showModal() { + // Dialog must not be `open` initially, prevent the following error: + // "Cannot call showModal() on an open non-modal dialog" + // Close the dialog first, to transition from non-modal to modal. + // Ideally we would't `close()` the dialog when we know the dialog is already modal. + this.dialogElement?.close(); + this.dialogElement?.showModal(); + } +} diff --git a/proprietary/design-tokens/src/component/utrecht/drawer.tokens.json b/proprietary/design-tokens/src/component/utrecht/drawer.tokens.json new file mode 100644 index 00000000000..9ecd9f8793f --- /dev/null +++ b/proprietary/design-tokens/src/component/utrecht/drawer.tokens.json @@ -0,0 +1,20 @@ +{ + "utrecht": { + "drawer": { + "background-color": { "value": "{utrecht.document.background-color}" }, + "border-color": { "value": "{utrecht.color.grey.90}" }, + "border-radius": {}, + "border-width": { "value": "1px" }, + "color": { "value": "{utrecht.document.color}" }, + "max-block-size": { "value": "240px" }, + "min-block-size": {}, + "max-inline-size": { "value": "320px" }, + "min-inline-size": {}, + "padding-block-start": { "value": "{utrecht.space.block.md}" }, + "padding-block-end": { "value": "{utrecht.space.block.md}" }, + "padding-inline-start": { "value": "{utrecht.space.inline.md}" }, + "padding-inline-end": { "value": "{utrecht.space.inline.md}" }, + "z-index": {} + } + } +}