mirror of
https://github.com/primer/css.git
synced 2024-11-12 22:06:08 +03:00
Overlay component (#1998)
* Add styles, documentation, and storybook story for the new dialog component * Stylelint auto-fixes * Create slow-poets-mate.md * Update slow-poets-mate.md * update namespace * Stylelint auto-fixes * menu specifics * starting over * a new paradigm * fully responsive * push for testing * auto width, visibility hidden * Overlay stories * dialog stories * cleanup * remove popover * remove unused import * docs update * min() to the rescue! * thin scrollbar? * remove comment * Update src/overlay/overlay.scss Co-authored-by: simurai <simurai@github.com> * address feedback * handle form elements * test permissions * lint Co-authored-by: Actions Auto Build <actions@github.com> Co-authored-by: Katie Langerman <langermank@github.com> Co-authored-by: simurai <simurai@github.com>
This commit is contained in:
parent
ab6f0840f0
commit
51e087aa31
5
.changeset/slow-poets-mate.md
Normal file
5
.changeset/slow-poets-mate.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@primer/css": minor
|
||||
---
|
||||
|
||||
Styles for the new Dialog Component
|
58
docs/content/components/dialog.md
Normal file
58
docs/content/components/dialog.md
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Dialog
|
||||
path: components/dialog
|
||||
status: Alpha
|
||||
source: 'https://github.com/github/github/tree/master/app/assets/stylesheets/experiments/modal-dialog.scss'
|
||||
bundle: dialog
|
||||
---
|
||||
|
||||
Please note that the `<div>` element with `id="fake-container"` is not included in the component.
|
||||
|
||||
```html live
|
||||
<div id="fake-container" style="height: 400px;">
|
||||
<button class="btn"><span>Open dialog</span></button>
|
||||
<div
|
||||
id="overlay-backdrop"
|
||||
class="Overlay-backdrop Overlay-backdrop--center"
|
||||
role="dialog"
|
||||
aria-labelledby="title-id"
|
||||
aria-describedby="description-id"
|
||||
data-focus-trap="active"
|
||||
>
|
||||
<div
|
||||
class="Overlay Overlay--width-medium Overlay--height-medium Overlay--motion-scaleFade"
|
||||
data-focus-trap="active"
|
||||
open=""
|
||||
>
|
||||
<header class="Overlay-header">
|
||||
<div class="Overlay-headerContentWrap">
|
||||
<div class="Overlay-titleWrap">
|
||||
<h1 id="title-id" class="Overlay-title">This is the title of the dialog</h1>
|
||||
<h2 id="description-id" class="Overlay-description">This is the subtitle of the dialog</h2>
|
||||
</div>
|
||||
<div class="Overlay-actionWrap">
|
||||
<button class="Overlay-closeButton" aria-label="Close">
|
||||
<svg aria-hidden="true" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="Overlay-body">
|
||||
This is the body of the dialogThis is the body of the dialogThis is the body of the dialog This is the body of
|
||||
the dialog This is the body of the dialog This is the body of the dialog This is the body of the dialog This is
|
||||
the body of the dialog This is the body of the dialog
|
||||
</div>
|
||||
<footer class="Overlay-footer Overlay-footer--divided Overlay-footer--alignEnd">
|
||||
<button class="btn"><span>Continue</span></button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
[aria attributes]: https://www.w3.org/TR/html-aria/#allowed-aria-roles-states-and-properties
|
388
docs/src/stories/components/Dialog/Dialog.stories.jsx
Normal file
388
docs/src/stories/components/Dialog/Dialog.stories.jsx
Normal file
@ -0,0 +1,388 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import {OverlayTemplate} from '../../ui-patterns/Overlay/Overlay.stories'
|
||||
|
||||
export default {
|
||||
title: 'Components/Dialog',
|
||||
parameters: {
|
||||
layout: 'padded'
|
||||
},
|
||||
|
||||
excludeStories: ['DialogTemplate'],
|
||||
argTypes: {
|
||||
title: {
|
||||
name: 'title',
|
||||
type: {name: 'string', required: false},
|
||||
description: 'The heading element of the dialog',
|
||||
defaultValue: '',
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
description: {
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
description: 'The sub-heading element of the dialog',
|
||||
defaultValue: '',
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
toggleOverlay: {
|
||||
control: {type: 'boolean'},
|
||||
description: 'show/hide overlay',
|
||||
defaultValue: false,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
showCloseButton: {
|
||||
control: {type: 'boolean'},
|
||||
description: 'show/hide close button',
|
||||
defaultValue: true,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
showFooterButton: {
|
||||
control: {type: 'boolean'},
|
||||
description: 'show/hide footer button',
|
||||
defaultValue: false,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
width: {
|
||||
options: [0, 1, 2, 3, 4, 5], // iterator
|
||||
mapping: [
|
||||
'Overlay--width-auto',
|
||||
'Overlay--width-small',
|
||||
'Overlay--width-medium',
|
||||
'Overlay--width-large',
|
||||
'Overlay--width-xlarge',
|
||||
'Overlay--width-xxlarge'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['auto', 'small', 'medium', 'large', 'xlarge', 'xxlarge']
|
||||
},
|
||||
description: 'Width options: small: 256px, medium: 320px, large: 480px, xlarge: 640px, xxlarge: 960px',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
height: {
|
||||
options: [0, 1, 2, 3, 4, 5], // iterator
|
||||
mapping: [
|
||||
'Overlay--height-auto',
|
||||
'Overlay--height-xsmall',
|
||||
'Overlay--height-small',
|
||||
'Overlay--height-medium',
|
||||
'Overlay--height-large',
|
||||
'Overlay--height-xlarge'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['auto', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge']
|
||||
},
|
||||
description:
|
||||
'Height options: auto: adjusts to content, xsmall: 192px, small: 256px, medium: 320px, large: 432px, xlarge: 600px',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
headerVariant: {
|
||||
options: [0, 1], // iterator
|
||||
mapping: ['', 'Overlay-header--large'], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['medium (default)', 'large']
|
||||
},
|
||||
description: 'medium (default), large header/description font-size + spacing',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
bodyPaddingVariant: {
|
||||
options: [0, 1, 2], // iterator
|
||||
mapping: ['', 'Overlay-body--paddingCondensed', 'Overlay-body--paddingNone'], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['normal (default)', 'condensed', 'none']
|
||||
},
|
||||
description: 'body spacing',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
variantNarrow: {
|
||||
options: [0, 1, 2, 3], // iterator
|
||||
mapping: [
|
||||
'Overlay-backdrop--center-whenNarrow',
|
||||
'Overlay-backdrop--anchor-whenNarrow',
|
||||
'Overlay-backdrop--side-whenNarrow',
|
||||
'Overlay-backdrop--full-whenNarrow'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['center', 'anchored', 'side', 'full']
|
||||
},
|
||||
description: '',
|
||||
table: {
|
||||
category: 'Variant'
|
||||
}
|
||||
},
|
||||
variantRegular: {
|
||||
options: [0, 1, 2, 3], // iterator
|
||||
mapping: [
|
||||
'Overlay-backdrop--center',
|
||||
'Overlay-backdrop--anchor',
|
||||
'Overlay-backdrop--side',
|
||||
'Overlay-backdrop--full'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['center', 'anchored', 'side', 'full']
|
||||
},
|
||||
description: '',
|
||||
table: {
|
||||
category: 'Variant'
|
||||
}
|
||||
},
|
||||
placementNarrow: {
|
||||
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
mapping: [
|
||||
'Overlay-backdrop--placement-top-whenNarrow',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-bottom-whenNarrow',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-right-whenNarrow',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-left-whenNarrow',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
],
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: [
|
||||
'top',
|
||||
'top-start',
|
||||
'top-center',
|
||||
'top-end',
|
||||
'bottom',
|
||||
'bottom-start',
|
||||
'bottom-center',
|
||||
'bottom-end',
|
||||
'right',
|
||||
'right-start',
|
||||
'right-center',
|
||||
'right-end',
|
||||
'left',
|
||||
'left-start',
|
||||
'left-center',
|
||||
'left-end',
|
||||
'reset'
|
||||
]
|
||||
},
|
||||
description: 'Positions overlay for narrow viewport range',
|
||||
table: {
|
||||
category: 'Placement'
|
||||
}
|
||||
},
|
||||
placementRegular: {
|
||||
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
mapping: [
|
||||
'Overlay-backdrop--placement-top',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-bottom',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-right',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-left',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
],
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: [
|
||||
'top',
|
||||
'top-start',
|
||||
'top-center',
|
||||
'top-end',
|
||||
'bottom',
|
||||
'bottom-start',
|
||||
'bottom-center',
|
||||
'bottom-end',
|
||||
'right',
|
||||
'right-start',
|
||||
'right-center',
|
||||
'right-end',
|
||||
'left',
|
||||
'left-start',
|
||||
'left-center',
|
||||
'left-end',
|
||||
'reset'
|
||||
]
|
||||
},
|
||||
description: 'Positions overlay for narrow viewport range',
|
||||
table: {
|
||||
category: 'Placement'
|
||||
}
|
||||
},
|
||||
headerRegion: {
|
||||
control: {type: 'boolean'},
|
||||
description:
|
||||
'A header region may be used to provide context to the user by displaying a title, description, and offering an easy-to-escape route with a Close button. Headers may also provide ways for the user to interact with the content, such as with search and tabs.',
|
||||
defaultValue: true,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
footerRegion: {
|
||||
control: {type: 'boolean'},
|
||||
description:
|
||||
'The footer region may be used to show confirmation actions, navigation links, or other important elements that should appear outside of the content scrolling region.',
|
||||
defaultValue: true,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
showFooterDivider: {
|
||||
control: {type: 'boolean'},
|
||||
defaultValue: false,
|
||||
description: 'Show dividers above footer',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
showHeaderDivider: {
|
||||
control: {type: 'boolean'},
|
||||
defaultValue: false,
|
||||
description: 'Show dividers below header',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
headerContentSlot: {
|
||||
description: 'Slot for additional header content',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
actionContentSlot: {
|
||||
description: 'Slot for additional header action',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
motion: {
|
||||
options: [0, 1], // iterator
|
||||
mapping: [null, 'Overlay--motion-scaleFade'], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['none', 'scaleFade']
|
||||
},
|
||||
description: 'Animation options for show/hide overlay',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
footerContentAlign: {
|
||||
options: [0, 1, 2], // iterator
|
||||
mapping: ['Overlay-footer--alignStart', 'Overlay-footer--alignCenter', 'Overlay-footer--alignEnd'], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['start', 'center', 'end']
|
||||
},
|
||||
description: 'Align footer contents',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
role: {
|
||||
description: 'Semantic role',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
ariaLabelledy: {
|
||||
description: 'aria-labelledby',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
ariaDescribedby: {
|
||||
description: 'aria-describedby',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
dataFocusTrap: {
|
||||
description: 'data-focus-trap',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
titleId: {
|
||||
description: 'title id',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
descriptionId: {
|
||||
description: 'description id',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const Playground = OverlayTemplate.bind({})
|
||||
Playground.args = {
|
||||
...OverlayTemplate.args,
|
||||
title: 'Dialog title',
|
||||
description: 'Optional dialog description',
|
||||
role: 'dialog',
|
||||
width: 2,
|
||||
height: 3,
|
||||
ariaLabelledby: 'title-id',
|
||||
ariaDescribedby: 'description-id',
|
||||
dataFocusTrap: 'active',
|
||||
footerContentAlign: 2,
|
||||
showCloseButton: true,
|
||||
headerVariant: 0,
|
||||
bodyPaddingVariant: 0,
|
||||
motion: 1,
|
||||
descriptionId: 'description-id',
|
||||
titleId: 'title-id',
|
||||
showFooterDivider: false,
|
||||
children: <p>Dialog body</p>,
|
||||
headerRegion: true,
|
||||
variantNarrow: 3,
|
||||
variantRegular: 0
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
// import { StoryTemplateName } from './OtherStoryFile.stories' // import stories for component compositions
|
||||
|
||||
export default {
|
||||
title: 'Components/Popover',
|
||||
excludeStories: ['PopoverTemplateName'],
|
||||
layout: 'padded',
|
||||
argTypes: {
|
||||
size: {
|
||||
options: [0, 1], // iterator
|
||||
mapping: ['', 'Popover-message--large'], // values
|
||||
control: {
|
||||
type: 'select',
|
||||
labels: ['default', 'large']
|
||||
},
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
caretPosition: {
|
||||
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // iterator
|
||||
mapping: [
|
||||
'',
|
||||
'Popover-message--bottom',
|
||||
'Popover-message--bottom-left',
|
||||
'Popover-message--bottom-right',
|
||||
'Popover-message--left',
|
||||
'Popover-message--left-bottom',
|
||||
'Popover-message--left-top',
|
||||
'Popover-message--right',
|
||||
'Popover-message--right-bottom',
|
||||
'Popover-message--right-top',
|
||||
'Popover-message--top-left',
|
||||
'Popover-message--top-right',
|
||||
'Popover-message--no-caret'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: [
|
||||
'default (top)',
|
||||
'bottom',
|
||||
'bottom_left',
|
||||
'bottom_right',
|
||||
'left',
|
||||
'left_bottom',
|
||||
'left_top',
|
||||
'right',
|
||||
'right_bottom',
|
||||
'right_top',
|
||||
'top_left',
|
||||
'top_right',
|
||||
'none'
|
||||
]
|
||||
},
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
messagePosition: {
|
||||
options: ['position-relative', 'position-absolute'],
|
||||
control: {
|
||||
type: 'inline-radio'
|
||||
},
|
||||
description: '',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
headingText: {
|
||||
name: 'headingText',
|
||||
type: 'string',
|
||||
description: 'string',
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
primerUtilities: {
|
||||
name: 'headingText',
|
||||
type: 'string',
|
||||
description: 'Primer utility classes',
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
tag: {
|
||||
options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
||||
control: {type: 'inline-radio'},
|
||||
description: 'h4 default',
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const PopoverTemplateName = ({
|
||||
size,
|
||||
caretPosition,
|
||||
messagePosition,
|
||||
headingText,
|
||||
tag,
|
||||
children,
|
||||
trigger,
|
||||
triggerBottom,
|
||||
primerUtilities
|
||||
}) => (
|
||||
<>
|
||||
{trigger}
|
||||
<div className={clsx('Popover', 'right-0', 'left-0', `${messagePosition}`)}>
|
||||
<div
|
||||
className={clsx('Popover-message', 'color-shadow-large', `${primerUtilities}`, `${size}`, `${caretPosition}`)}
|
||||
>
|
||||
{tag === 'h1' && <h1 className="mb-2">{headingText}</h1>}
|
||||
{tag === 'h2' && <h2 className="mb-2">{headingText}</h2>}
|
||||
{tag === 'h3' && <h3 className="mb-2">{headingText}</h3>}
|
||||
{tag === 'h4' && <h4 className="mb-2">{headingText}</h4>}
|
||||
{tag === 'h5' && <h5 className="mb-2">{headingText}</h5>}
|
||||
{tag === 'h6' && <h6 className="mb-2">{headingText}</h6>}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
{triggerBottom}
|
||||
</>
|
||||
)
|
||||
|
||||
export const Playground = PopoverTemplateName.bind({})
|
||||
Playground.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: '',
|
||||
messagePosition: 'position-relative',
|
||||
primerUtilities: 'text-left p-4 mt-2 mx-aut'
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import {PopoverTemplateName} from './Popover.stories.jsx'
|
||||
|
||||
export default {
|
||||
title: 'Components/Popover/Features',
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/GCvY3Qv8czRgZgvl1dG6lp/Primer-Web?node-id=410%3A3890'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const Bottom = PopoverTemplateName.bind({})
|
||||
Bottom.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--bottom',
|
||||
messagePosition: 'position-relative',
|
||||
triggerBottom: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 mx-auto mb-2 text-left'
|
||||
}
|
||||
Bottom.decorators = [
|
||||
Story => (
|
||||
<div className="position-relative text-center">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const BottomRight = PopoverTemplateName.bind({})
|
||||
BottomRight.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--bottom-right',
|
||||
messagePosition: 'position-relative',
|
||||
triggerBottom: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 mb-2 text-left'
|
||||
}
|
||||
BottomRight.decorators = [
|
||||
Story => (
|
||||
<div className="position-relative text-right">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const BottomLeft = PopoverTemplateName.bind({})
|
||||
BottomLeft.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--bottom-left',
|
||||
messagePosition: 'position-relative',
|
||||
triggerBottom: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 mb-2'
|
||||
}
|
||||
BottomLeft.decorators = [
|
||||
Story => (
|
||||
<div className="position-relative">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const Left = PopoverTemplateName.bind({})
|
||||
Left.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--left',
|
||||
messagePosition: 'position-relative',
|
||||
trigger: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 ml-2'
|
||||
}
|
||||
Left.decorators = [
|
||||
Story => (
|
||||
<div className="d-flex flex-justify-center flex-items-center">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const LeftBottom = PopoverTemplateName.bind({})
|
||||
LeftBottom.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--left-bottom',
|
||||
messagePosition: 'position-relative',
|
||||
trigger: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 ml-2'
|
||||
}
|
||||
LeftBottom.decorators = [
|
||||
Story => (
|
||||
<div className="d-flex flex-justify-center flex-items-end">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const LeftTop = PopoverTemplateName.bind({})
|
||||
LeftTop.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--left-top',
|
||||
messagePosition: 'position-relative',
|
||||
trigger: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 ml-2'
|
||||
}
|
||||
LeftTop.decorators = [
|
||||
Story => (
|
||||
<div className="d-flex flex-justify-center flex-items-start">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const Right = PopoverTemplateName.bind({})
|
||||
Right.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--right',
|
||||
messagePosition: 'position-relative',
|
||||
triggerBottom: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 mr-2'
|
||||
}
|
||||
Right.decorators = [
|
||||
Story => (
|
||||
<div className="d-flex flex-justify-center flex-items-center">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const RightBottom = PopoverTemplateName.bind({})
|
||||
RightBottom.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--right-bottom',
|
||||
messagePosition: 'position-relative',
|
||||
triggerBottom: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 mr-2'
|
||||
}
|
||||
RightBottom.decorators = [
|
||||
Story => (
|
||||
<div className="d-flex flex-justify-center flex-items-end">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const RightTop = PopoverTemplateName.bind({})
|
||||
RightTop.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--right-top',
|
||||
messagePosition: 'position-relative',
|
||||
triggerBottom: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 mr-2'
|
||||
}
|
||||
RightTop.decorators = [
|
||||
Story => (
|
||||
<div className="d-flex flex-justify-center flex-items-start">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const TopLeft = PopoverTemplateName.bind({})
|
||||
TopLeft.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--top-left',
|
||||
messagePosition: 'position-relative',
|
||||
trigger: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 mt-2'
|
||||
}
|
||||
TopLeft.decorators = [
|
||||
Story => (
|
||||
<div className="position-relative pl-2">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const TopRight = PopoverTemplateName.bind({})
|
||||
TopRight.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: '',
|
||||
caretPosition: 'Popover-message--top-right',
|
||||
messagePosition: 'position-relative',
|
||||
trigger: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'text-left p-4 mt-2'
|
||||
}
|
||||
TopRight.decorators = [
|
||||
Story => (
|
||||
<div className="position-relative text-right pr-2">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
|
||||
export const Large = PopoverTemplateName.bind({})
|
||||
Large.args = {
|
||||
tag: 'h4',
|
||||
headingText: 'Popover heading',
|
||||
size: 'Popover-message--large',
|
||||
caretPosition: 'Popover-message--bottom',
|
||||
messagePosition: 'position-relative',
|
||||
triggerBottom: [<button class="btn btn-primary">Trigger button</button>],
|
||||
primerUtilities: 'p-4 mx-auto mb-2 text-left'
|
||||
}
|
||||
Large.decorators = [
|
||||
Story => (
|
||||
<div className="position-relative text-center">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
518
docs/src/stories/ui-patterns/Overlay/Overlay.stories.jsx
Normal file
518
docs/src/stories/ui-patterns/Overlay/Overlay.stories.jsx
Normal file
@ -0,0 +1,518 @@
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
import ConditionalWrapper from '../../helpers/ConditionalWrapper'
|
||||
import {PatternFullBleed} from '../ActionList/ActionListFeatures.stories.jsx'
|
||||
const variant = {}
|
||||
export default {
|
||||
title: 'UI Patterns/Overlay',
|
||||
parameters: {
|
||||
layout: 'padded'
|
||||
},
|
||||
excludeStories: ['OverlayTemplate'],
|
||||
argTypes: {
|
||||
title: {
|
||||
name: 'title',
|
||||
type: {name: 'string', required: false},
|
||||
description: 'The heading element of the dialog',
|
||||
defaultValue: '',
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
description: {
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
description: 'The sub-heading element of the dialog',
|
||||
defaultValue: '',
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
toggleOverlay: {
|
||||
control: {type: 'boolean'},
|
||||
description: 'show/hide overlay',
|
||||
defaultValue: false,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
showCloseButton: {
|
||||
control: {type: 'boolean'},
|
||||
description: 'show/hide close button',
|
||||
defaultValue: false,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
showFooterButton: {
|
||||
control: {type: 'boolean'},
|
||||
description: 'show/hide footer button',
|
||||
defaultValue: false,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
width: {
|
||||
options: [0, 1, 2, 3, 4, 5], // iterator
|
||||
mapping: [
|
||||
'Overlay--width-auto',
|
||||
'Overlay--width-small',
|
||||
'Overlay--width-medium',
|
||||
'Overlay--width-large',
|
||||
'Overlay--width-xlarge',
|
||||
'Overlay--width-xxlarge'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['auto', 'small', 'medium', 'large', 'xlarge', 'xxlarge']
|
||||
},
|
||||
description: 'Width options: small: 256px, medium: 320px, large: 480px, xlarge: 640px, xxlarge: 960px',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
height: {
|
||||
options: [0, 1, 2, 3, 4, 5], // iterator
|
||||
mapping: [
|
||||
'Overlay--height-auto',
|
||||
'Overlay--height-xsmall',
|
||||
'Overlay--height-small',
|
||||
'Overlay--height-medium',
|
||||
'Overlay--height-large',
|
||||
'Overlay--height-xlarge'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['auto', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge']
|
||||
},
|
||||
description:
|
||||
'Height options: auto: adjusts to content, xsmall: 192px, small: 256px, medium: 320px, large: 432px, xlarge: 600px',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
headerVariant: {
|
||||
options: [0, 1], // iterator
|
||||
mapping: ['', 'Overlay-header--large'], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['medium (default)', 'large']
|
||||
},
|
||||
description: 'medium (default), large header/description font-size + spacing',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
bodyPaddingVariant: {
|
||||
options: [0, 1, 2], // iterator
|
||||
mapping: ['', 'Overlay-body--paddingCondensed', 'Overlay-body--paddingNone'], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['normal (default)', 'condensed', 'none']
|
||||
},
|
||||
description: 'body spacing',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
variantNarrow: {
|
||||
options: [0, 1, 2, 3], // iterator
|
||||
mapping: [
|
||||
'Overlay-backdrop--center-whenNarrow',
|
||||
'Overlay-backdrop--anchor-whenNarrow',
|
||||
'Overlay-backdrop--side-whenNarrow',
|
||||
'Overlay-backdrop--full-whenNarrow'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['center', 'anchored', 'side', 'full']
|
||||
},
|
||||
description: '',
|
||||
table: {
|
||||
category: 'Variant'
|
||||
}
|
||||
},
|
||||
variantRegular: {
|
||||
options: [0, 1, 2, 3], // iterator
|
||||
mapping: [
|
||||
'Overlay-backdrop--center',
|
||||
'Overlay-backdrop--anchor',
|
||||
'Overlay-backdrop--side',
|
||||
'Overlay-backdrop--full'
|
||||
], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['center', 'anchored', 'side', 'full']
|
||||
},
|
||||
description: '',
|
||||
table: {
|
||||
category: 'Variant'
|
||||
}
|
||||
},
|
||||
placementNarrow: {
|
||||
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
mapping: [
|
||||
'Overlay-backdrop--placement-top-whenNarrow',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-bottom-whenNarrow',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-right-whenNarrow',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-left-whenNarrow',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
],
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: [
|
||||
'top',
|
||||
'top-start',
|
||||
'top-center',
|
||||
'top-end',
|
||||
'bottom',
|
||||
'bottom-start',
|
||||
'bottom-center',
|
||||
'bottom-end',
|
||||
'right',
|
||||
'right-start',
|
||||
'right-center',
|
||||
'right-end',
|
||||
'left',
|
||||
'left-start',
|
||||
'left-center',
|
||||
'left-end',
|
||||
'reset'
|
||||
]
|
||||
},
|
||||
description: 'Positions overlay for narrow viewport range',
|
||||
table: {
|
||||
category: 'Placement'
|
||||
}
|
||||
},
|
||||
placementRegular: {
|
||||
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
mapping: [
|
||||
'Overlay-backdrop--placement-top',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-bottom',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-right',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Overlay-backdrop--placement-left',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
],
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: [
|
||||
'top',
|
||||
'top-start',
|
||||
'top-center',
|
||||
'top-end',
|
||||
'bottom',
|
||||
'bottom-start',
|
||||
'bottom-center',
|
||||
'bottom-end',
|
||||
'right',
|
||||
'right-start',
|
||||
'right-center',
|
||||
'right-end',
|
||||
'left',
|
||||
'left-start',
|
||||
'left-center',
|
||||
'left-end',
|
||||
'reset'
|
||||
]
|
||||
},
|
||||
description: 'Positions overlay for narrow viewport range',
|
||||
table: {
|
||||
category: 'Placement'
|
||||
}
|
||||
},
|
||||
headerRegion: {
|
||||
control: {type: 'boolean'},
|
||||
description:
|
||||
'A header region may be used to provide context to the user by displaying a title, description, and offering an easy-to-escape route with a Close button. Headers may also provide ways for the user to interact with the content, such as with search and tabs.',
|
||||
defaultValue: true,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
footerRegion: {
|
||||
control: {type: 'boolean'},
|
||||
description:
|
||||
'The footer region may be used to show confirmation actions, navigation links, or other important elements that should appear outside of the content scrolling region.',
|
||||
defaultValue: true,
|
||||
table: {
|
||||
category: 'Demo'
|
||||
}
|
||||
},
|
||||
showFooterDivider: {
|
||||
control: {type: 'boolean'},
|
||||
defaultValue: false,
|
||||
description: 'Show dividers above footer',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
showHeaderDivider: {
|
||||
control: {type: 'boolean'},
|
||||
defaultValue: false,
|
||||
description: 'Show dividers below header',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
headerContentSlot: {
|
||||
description: 'Slot for additional header content',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
actionContentSlot: {
|
||||
description: 'Slot for additional header action',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
motion: {
|
||||
options: [0, 1], // iterator
|
||||
mapping: [null, 'Overlay--motion-scaleFade'], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['none', 'scaleFade']
|
||||
},
|
||||
description: 'Animation options for show/hide overlay',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
footerContentAlign: {
|
||||
options: [0, 1, 2], // iterator
|
||||
mapping: ['Overlay-footer--alignStart', 'Overlay-footer--alignCenter', 'Overlay-footer--alignEnd'], // values
|
||||
control: {
|
||||
type: 'inline-radio',
|
||||
labels: ['start', 'center', 'end']
|
||||
},
|
||||
description: 'Align footer contents',
|
||||
table: {
|
||||
category: 'CSS'
|
||||
}
|
||||
},
|
||||
role: {
|
||||
description: 'Semantic role',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
ariaLabelledy: {
|
||||
description: 'aria-labelledby',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
ariaDescribedby: {
|
||||
description: 'aria-describedby',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
dataFocusTrap: {
|
||||
description: 'data-focus-trap',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
titleId: {
|
||||
description: 'title id',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
},
|
||||
descriptionId: {
|
||||
description: 'description id',
|
||||
control: {type: 'string'},
|
||||
table: {
|
||||
category: 'HTML'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const focusMethod = function getFocus() {
|
||||
const dialog = document.getElementById('overlay-backdrop')[0]
|
||||
dialog.focus()
|
||||
}
|
||||
|
||||
const toggleDialog = () => {
|
||||
const dialog = document.getElementById('overlay-backdrop')
|
||||
dialog.classList.toggle('Overlay--hidden')
|
||||
focusMethod()
|
||||
}
|
||||
|
||||
export const OverlayTemplate = ({
|
||||
title,
|
||||
description,
|
||||
toggleOverlay,
|
||||
width,
|
||||
height,
|
||||
showFooterDivider,
|
||||
showHeaderDivider,
|
||||
headerRegion,
|
||||
footerRegion,
|
||||
headerContentSlot,
|
||||
motion,
|
||||
footerContentAlign,
|
||||
showCloseButton,
|
||||
showFooterButton,
|
||||
actionContentSlot,
|
||||
headerVariant,
|
||||
bodyPaddingVariant,
|
||||
role,
|
||||
ariaLabelledby,
|
||||
ariaDescribedby,
|
||||
dataFocusTrap,
|
||||
children,
|
||||
titleId,
|
||||
descriptionId,
|
||||
variantNarrow,
|
||||
variantRegular,
|
||||
placementNarrow,
|
||||
placementRegular
|
||||
}) => (
|
||||
<>
|
||||
<button class="btn" onClick={toggleDialog}>
|
||||
<span>Open overlay</span>
|
||||
</button>
|
||||
<div
|
||||
id="overlay-backdrop"
|
||||
className={clsx(
|
||||
toggleOverlay && 'Overlay--hidden',
|
||||
variantNarrow && `${variantNarrow}`,
|
||||
variantRegular && `${variantRegular}`,
|
||||
placementNarrow && `${placementNarrow}`,
|
||||
placementRegular && `${placementRegular}`
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
'Overlay',
|
||||
width && `${width}`,
|
||||
height && `${height}`,
|
||||
motion && `${motion}`,
|
||||
variantNarrow && 'Overlay-whenNarrow'
|
||||
)}
|
||||
data-focus-trap={dataFocusTrap}
|
||||
role={role}
|
||||
aria-labelledby={ariaLabelledby}
|
||||
aria-describedby={ariaDescribedby}
|
||||
open
|
||||
>
|
||||
{headerRegion && (
|
||||
<header
|
||||
className={clsx(
|
||||
'Overlay-header',
|
||||
showHeaderDivider && 'Overlay-header--divided',
|
||||
headerVariant && `${headerVariant}`
|
||||
)}
|
||||
>
|
||||
<div className="Overlay-headerContentWrap">
|
||||
<div className="Overlay-titleWrap">
|
||||
{title && (
|
||||
<h1 id={titleId} className="Overlay-title">
|
||||
{title}
|
||||
</h1>
|
||||
)}
|
||||
{description && (
|
||||
<h2 id={descriptionId} className="Overlay-description">
|
||||
{description}
|
||||
</h2>
|
||||
)}
|
||||
</div>
|
||||
{showCloseButton && (
|
||||
<div className="Overlay-actionWrap">
|
||||
{actionContentSlot && <div dangerouslySetInnerHTML={{__html: actionContentSlot}} />}
|
||||
<button className="Overlay-closeButton" aria-label="Close" onClick={toggleDialog}>
|
||||
<svg aria-hidden="true" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{headerContentSlot && (
|
||||
<div className="Overlay-headerContentSlot" dangerouslySetInnerHTML={{__html: headerContentSlot}} />
|
||||
)}
|
||||
</header>
|
||||
)}
|
||||
<div className={clsx('Overlay-body', bodyPaddingVariant && `${bodyPaddingVariant}`)}>{children}</div>
|
||||
{footerRegion && (
|
||||
<footer
|
||||
className={clsx(
|
||||
'Overlay-footer',
|
||||
showFooterDivider && 'Overlay-footer--divided',
|
||||
footerContentAlign && `${footerContentAlign}`
|
||||
)}
|
||||
>
|
||||
{showFooterButton && (
|
||||
<button class="btn" onClick={toggleDialog}>
|
||||
<span>Continue</span>
|
||||
</button>
|
||||
)}
|
||||
</footer>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
export const Playground = OverlayTemplate.bind({})
|
||||
Playground.storyName = 'Playground'
|
||||
Playground.args = {
|
||||
title: 'This is the title of the dialog',
|
||||
description: 'This is the subtitle of the dialog',
|
||||
motion: 1,
|
||||
footerContentAlign: 2,
|
||||
showCloseButton: true,
|
||||
showFooterButton: false,
|
||||
headerContentSlot: '',
|
||||
actionContentSlot: '',
|
||||
headerVariant: 0,
|
||||
bodyPaddingVariant: 0,
|
||||
width: 1,
|
||||
height: 3,
|
||||
headerVariant: 0,
|
||||
headerRegion: true,
|
||||
footerRegion: true,
|
||||
showFooterDivider: false,
|
||||
showHeaderDivider: false,
|
||||
role: '',
|
||||
ariaDescribedby: '',
|
||||
dataFocusTrap: ''
|
||||
}
|
@ -24,6 +24,7 @@
|
||||
@import '../pagination/index.scss';
|
||||
@import '../tooltips/index.scss';
|
||||
@import '../truncate/index.scss';
|
||||
@import '../overlay/index.scss';
|
||||
|
||||
// Utilities always go last so that they can override components
|
||||
@import '../utilities/index.scss';
|
||||
|
24
src/overlay/README.md
Normal file
24
src/overlay/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
bundle: 'overlay'
|
||||
generated: true
|
||||
---
|
||||
|
||||
# Primer CSS: `overlay` bundle
|
||||
|
||||
## Usage
|
||||
|
||||
Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with:
|
||||
|
||||
```scss
|
||||
@import '@primer/css/overlay/index.scss';
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
The `@primer/css` npm package includes a standalone CSS build of this module in `dist/overlay.css`.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/)
|
||||
|
||||
[scss]: https://sass-lang.com/documentation/syntax#scss
|
2
src/overlay/index.scss
Normal file
2
src/overlay/index.scss
Normal file
@ -0,0 +1,2 @@
|
||||
@import '../support/index.scss';
|
||||
@import './overlay.scss';
|
384
src/overlay/overlay.scss
Normal file
384
src/overlay/overlay.scss
Normal file
@ -0,0 +1,384 @@
|
||||
// stylelint-disable selector-max-compound-selectors, max-nesting-depth, selector-max-specificity, primer/borders
|
||||
// replace with primitive
|
||||
$primer-borderRadius-large: 0.75rem;
|
||||
|
||||
.Overlay--hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.Overlay--visibilityHidden {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.Overlay {
|
||||
display: flex;
|
||||
min-width: 192px;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-canvas-overlay);
|
||||
border-radius: $primer-borderRadius-large;
|
||||
box-shadow: var(--color-overlay-shadow);
|
||||
opacity: 1;
|
||||
|
||||
&.Overlay--height-auto {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&.Overlay--height-xsmall {
|
||||
height: min(192px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--height-small {
|
||||
height: min(256px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--height-medium {
|
||||
height: min(320px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--height-large {
|
||||
height: min(432px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--height-xlarge {
|
||||
height: min(600px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--width-auto {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&.Overlay--width-small {
|
||||
width: min(256px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--width-medium {
|
||||
width: min(320px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--width-large {
|
||||
// stylelint-disable-next-line primer/responsive-widths
|
||||
width: min(480px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--width-xlarge {
|
||||
// stylelint-disable-next-line primer/responsive-widths
|
||||
width: min(640px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--width-xxlarge {
|
||||
// stylelint-disable-next-line primer/responsive-widths
|
||||
width: min(960px, 100% - 2rem);
|
||||
}
|
||||
|
||||
&.Overlay--motion-scaleFade {
|
||||
@media screen and (prefers-reduced-motion: no-preference) {
|
||||
animation: 200ms cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running Overlay--motion-scaleFade;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes Overlay--motion-scaleFade {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.5);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for <form> element that wraps entire contents of overlay
|
||||
.Overlay-form {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Overlay-header {
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.Overlay-header--divided {
|
||||
padding-bottom: $spacer-2;
|
||||
// stylelint-disable-next-line primer/box-shadow
|
||||
box-shadow: inset 0 #{-$border-width} var(--color-border-default);
|
||||
|
||||
+ .Overlay-body {
|
||||
padding-top: $spacer-3;
|
||||
}
|
||||
}
|
||||
|
||||
&.Overlay-header--large {
|
||||
.Overlay-headerContentWrap {
|
||||
.Overlay-titleWrap {
|
||||
gap: $spacer-2;
|
||||
|
||||
.Overlay-title {
|
||||
font-size: $h3-size;
|
||||
}
|
||||
|
||||
.Overlay-description {
|
||||
font-size: $body-font-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Overlay-headerContentWrap {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: $spacer-2;
|
||||
padding: $spacer-2 $spacer-2 0 $spacer-2;
|
||||
|
||||
.Overlay-actionWrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $spacer-2;
|
||||
}
|
||||
|
||||
.Overlay-titleWrap {
|
||||
display: flex;
|
||||
padding: ($spacer-2 * 0.75) 0 ($spacer-2 * 0.75) $spacer-2;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
gap: $spacer-1;
|
||||
|
||||
.Overlay-title {
|
||||
margin: 0;
|
||||
font-size: $body-font-size;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
.Overlay-description {
|
||||
margin: 0;
|
||||
font-size: $font-size-small;
|
||||
font-weight: $font-weight-normal;
|
||||
color: var(--color-fg-muted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generic body content slot
|
||||
.Overlay-body {
|
||||
padding: $spacer-3;
|
||||
padding-top: 0;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
font-size: $body-font-size;
|
||||
flex-grow: 1;
|
||||
|
||||
&.Overlay-body--paddingCondensed {
|
||||
padding: $spacer-2;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&.Overlay-body--paddingNone {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// generic footer slot
|
||||
.Overlay-footer {
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
padding: 0 $spacer-3 $spacer-3 $spacer-3;
|
||||
flex-direction: row;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.Overlay-footer--divided {
|
||||
padding-top: $spacer-3;
|
||||
// stylelint-disable-next-line primer/box-shadow
|
||||
box-shadow: inset 0 $border-width var(--color-border-default);
|
||||
}
|
||||
|
||||
&.Overlay-footer--alignStart {
|
||||
justify-content: flex-start;
|
||||
gap: $spacer-2;
|
||||
}
|
||||
|
||||
&.Overlay-footer--alignCenter {
|
||||
justify-content: center;
|
||||
gap: $spacer-2;
|
||||
}
|
||||
|
||||
&.Overlay-footer--alignEnd {
|
||||
justify-content: flex-end;
|
||||
gap: $spacer-2;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace with refactored IconButton
|
||||
.Overlay-closeButton {
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: $spacer-5;
|
||||
height: $spacer-5;
|
||||
padding: 0;
|
||||
color: var(--color-fg-muted);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background-color: transparent;
|
||||
border: $border-width $border-style transparent;
|
||||
border-radius: $border-radius;
|
||||
transition: 0.2s cubic-bezier(0.3, 0, 0.5, 1);
|
||||
transition-property: color, background-color, border-color;
|
||||
place-content: center;
|
||||
align-self: flex-start;
|
||||
flex-shrink: 0;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--color-btn-hover-bg);
|
||||
border: $border-width $border-style var(--color-btn-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin Overlay-backdrop() {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
background-color: var(--color-neutral-muted);
|
||||
}
|
||||
|
||||
@mixin Overlay-backdrop--transparent() {
|
||||
position: absolute;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
// variants must be mixins so we can extend within a media query (@extend is not supported inside media queries)
|
||||
|
||||
// border-radius repeats within placement options to ensure the original radius is reset when two classes co-exist
|
||||
|
||||
// center
|
||||
@mixin Overlay-backdrop--center {
|
||||
@include Overlay-backdrop;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// anchor
|
||||
@mixin Overlay-backdrop--anchor {
|
||||
@include Overlay-backdrop--transparent;
|
||||
|
||||
.Overlay {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// anchor side(s)
|
||||
@mixin Overlay-backdrop--side($responsiveVariant: '') {
|
||||
@include Overlay-backdrop;
|
||||
|
||||
// default left
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
|
||||
&.Overlay-backdrop--placement-left#{$responsiveVariant} {
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
|
||||
.Overlay#{$responsiveVariant} {
|
||||
border-radius: $primer-borderRadius-large;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.Overlay-backdrop--placement-right#{$responsiveVariant} {
|
||||
align-items: center;
|
||||
justify-content: right;
|
||||
|
||||
.Overlay#{$responsiveVariant} {
|
||||
border-radius: $primer-borderRadius-large;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.Overlay-backdrop--placement-bottom#{$responsiveVariant} {
|
||||
align-items: end;
|
||||
justify-content: center;
|
||||
|
||||
.Overlay#{$responsiveVariant} {
|
||||
border-radius: $primer-borderRadius-large;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.Overlay-backdrop--placement-top#{$responsiveVariant} {
|
||||
align-items: start;
|
||||
justify-content: center;
|
||||
|
||||
.Overlay#{$responsiveVariant} {
|
||||
border-radius: $primer-borderRadius-large;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// full width
|
||||
@mixin Overlay-backdrop--full {
|
||||
@include Overlay-backdrop;
|
||||
|
||||
.Overlay {
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
border-radius: unset;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay variant classnames
|
||||
.Overlay-backdrop--center {
|
||||
@include Overlay-backdrop--center;
|
||||
}
|
||||
|
||||
.Overlay-backdrop--anchor {
|
||||
@include Overlay-backdrop--anchor;
|
||||
}
|
||||
|
||||
.Overlay-backdrop--side {
|
||||
@include Overlay-backdrop--side;
|
||||
}
|
||||
|
||||
.Overlay-backdrop--full {
|
||||
@include Overlay-backdrop--full;
|
||||
}
|
||||
|
||||
// responsive variants
|
||||
// up to 767px
|
||||
@media (max-width: #{map-get($breakpoints, 'md') - 0.02px}) {
|
||||
.Overlay-backdrop--center-whenNarrow {
|
||||
@include Overlay-backdrop--center;
|
||||
}
|
||||
|
||||
.Overlay-backdrop--anchor-whenNarrow {
|
||||
@include Overlay-backdrop--anchor;
|
||||
}
|
||||
|
||||
.Overlay-backdrop--side-whenNarrow {
|
||||
@include Overlay-backdrop--side('-whenNarrow');
|
||||
}
|
||||
|
||||
.Overlay-backdrop--full-whenNarrow {
|
||||
@include Overlay-backdrop--full;
|
||||
}
|
||||
}
|
@ -24,4 +24,4 @@
|
||||
@import '../select-menu/index.scss';
|
||||
@import '../subhead/index.scss';
|
||||
@import '../timeline/index.scss';
|
||||
@import '../toasts/index.scss'
|
||||
@import '../toasts/index.scss';
|
||||
|
Loading…
Reference in New Issue
Block a user