Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfee5a822e | ||
|
|
58ed3b5367 | ||
|
|
00b3dd7d59 | ||
|
|
90db7dd0c1 | ||
|
|
b6d0304d5a | ||
|
|
de0a797e09 | ||
|
|
c4eaf0f350 | ||
|
|
fb9897e6a0 | ||
|
|
3438b5d7e7 | ||
|
|
3583062824 | ||
|
|
2993f809ad |
No files matched your search
@@ -1,5 +1,11 @@
|
||||
# @navikt/aksel-icons
|
||||
|
||||
## 5.0.2
|
||||
|
||||
## 5.0.1
|
||||
|
||||
## 5.0.0
|
||||
|
||||
## 4.12.1
|
||||
|
||||
## 4.12.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-icons",
|
||||
"version": "4.12.1",
|
||||
"version": "5.0.2",
|
||||
"author": "Aksel | NAV",
|
||||
"homepage": "https://aksel.nav.no/ikoner",
|
||||
"repository": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @navikt/aksel-stylelint
|
||||
|
||||
## 5.0.2
|
||||
|
||||
## 5.0.1
|
||||
|
||||
## 5.0.0
|
||||
|
||||
## 4.12.1
|
||||
|
||||
## 4.12.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-stylelint",
|
||||
"version": "4.12.1",
|
||||
"version": "5.0.2",
|
||||
"author": "Aksel | NAV",
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
|
||||
"repository": {
|
||||
@@ -35,8 +35,8 @@
|
||||
"dev": "yarn watch:lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-css": "^4.12.1",
|
||||
"@navikt/ds-tokens": "^4.12.1",
|
||||
"@navikt/ds-css": "^5.0.2",
|
||||
"@navikt/ds-tokens": "^5.0.2",
|
||||
"@types/jest": "^29.0.0",
|
||||
"concurrently": "7.2.1",
|
||||
"copyfiles": "2.4.1",
|
||||
|
||||
@@ -25,4 +25,17 @@ export const deprecations: DeprecatedList = [
|
||||
message:
|
||||
"In v4.1.0 Chips. Toggle no longer handles special alignment for checkmark-icon, thus removing this class",
|
||||
},
|
||||
{
|
||||
classes: ["navds-modal__overlay"],
|
||||
message:
|
||||
"Removed in v5.0.0, use `.navds-modal::backdrop` and `.navds-modal--polyfilled + .backdrop` instead",
|
||||
},
|
||||
{
|
||||
classes: ["navds-modal__button--shake"],
|
||||
message: "Removed in v5.0.0",
|
||||
},
|
||||
{
|
||||
classes: ["navds-modal__content"],
|
||||
message: "Renamed to `.navds-modal__body` in v5.0.0",
|
||||
},
|
||||
];
|
||||
@@ -1,5 +1,26 @@
|
||||
# @navikt/aksel
|
||||
|
||||
## 5.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.0.2
|
||||
|
||||
## 5.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`90db7dd0c`](https://github.com/navikt/aksel/commit/90db7dd0c120c16a387d3169c05c1f33dc694323)]:
|
||||
- @navikt/ds-css@5.0.1
|
||||
|
||||
## 5.0.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`2993f809a`](https://github.com/navikt/aksel/commit/2993f809adbf26fe88237066fea7f3e1a21b6690), [`2993f809a`](https://github.com/navikt/aksel/commit/2993f809adbf26fe88237066fea7f3e1a21b6690), [`3438b5d7e`](https://github.com/navikt/aksel/commit/3438b5d7e75037889946d4f89e95cd1f91bbb2d0)]:
|
||||
- @navikt/ds-css@5.0.0
|
||||
|
||||
## 4.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel",
|
||||
"version": "4.12.1",
|
||||
"version": "5.0.2",
|
||||
"description": "Aksel command line interface. Handles css-imports, codemods and more",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "4.12.1",
|
||||
"@navikt/ds-css": "5.0.2",
|
||||
"axios": "1.3.6",
|
||||
"chalk": "4.1.0",
|
||||
"clipboardy": "^2.3.0",
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 5.0.2
|
||||
|
||||
## 5.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Checkbox: Hotfix da checkmark ikke var sentrert hvis man brukte tailwind ([`90db7dd0c`](https://github.com/navikt/aksel/commit/90db7dd0c120c16a387d3169c05c1f33dc694323))
|
||||
|
||||
## 5.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- Oppdatert Modal - Se [Migrering](https://aksel.nav.no/grunnleggende/kode/migrering) ([#2135](https://github.com/navikt/aksel/pull/2135))
|
||||
|
||||
- :sparkles: Støtte for header og footer
|
||||
- :boom: Bruker nå native dialog i stedet for react-modal (med polyfill for eldre nettlesere)
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Table: ExpandableRow har oppdatert knapp for å matche Accordion og ExpansionCard ([#2178](https://github.com/navikt/aksel/pull/2178))
|
||||
|
||||
- Table: Alle størrelser har justert padding. Small-size table bruker nå også standard typografi-størrelse (18px). ([#2178](https://github.com/navikt/aksel/pull/2178))
|
||||
|
||||
## 4.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -121,7 +121,7 @@ const StyleMappings = {
|
||||
{
|
||||
component: "Modal",
|
||||
main: "modal.css",
|
||||
dependencies: ["button.css"],
|
||||
dependencies: [typoCss, "button.css"],
|
||||
},
|
||||
{
|
||||
component: "Pagination",
|
||||
|
||||
@@ -135,6 +135,11 @@
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
/* Tailwind sets all svg to block */
|
||||
.navds-checkbox__icon > svg {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.navds-checkbox--small > .navds-checkbox__input:checked + .navds-checkbox__label::before {
|
||||
background-position: 0.25rem center;
|
||||
}
|
||||
|
||||
@@ -1,69 +1,152 @@
|
||||
.ReactModal__Body--open {
|
||||
.navds-modal__document-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.navds-modal {
|
||||
background-color: var(--ac-modal-bg, var(--a-surface-default));
|
||||
border-radius: var(--a-border-radius-medium);
|
||||
display: block;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
border: 1px solid var(--ac-modal-border, var(--a-border-subtle));
|
||||
border-radius: var(--a-border-radius-large);
|
||||
box-shadow: var(--a-shadow-xlarge);
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.navds-modal__content {
|
||||
padding: var(--a-spacing-4);
|
||||
.navds-modal[open] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
animation: akselModalFadeIn 0.35s cubic-bezier(0.15, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.navds-modal:focus-visible,
|
||||
.navds-modal--focus {
|
||||
box-shadow: var(--a-shadow-focus);
|
||||
outline: none;
|
||||
.navds-modal--polyfilled {
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
overflow: auto;
|
||||
|
||||
/* From polyfill (dialog-polyfill/dist/dialog-polyfill.css): */
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-modal:focus,
|
||||
.navds-modal--focus {
|
||||
box-shadow: var(--a-shadow-focus);
|
||||
outline: none;
|
||||
.navds-modal--polyfilled:not([open]) {
|
||||
display: none; /* from polyfill */
|
||||
}
|
||||
|
||||
._dialog_overlay {
|
||||
/* From polyfill */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.navds-modal--small {
|
||||
width: var(--ac-modal-width-small, 450px);
|
||||
}
|
||||
|
||||
.navds-modal--medium {
|
||||
width: var(--ac-modal-width-medium, 700px);
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
.navds-modal {
|
||||
max-width: calc((100% - 6px) - 2em);
|
||||
max-height: calc((100% - 6px) - 2em);
|
||||
}
|
||||
|
||||
.navds-modal--autowidth {
|
||||
max-width: min(700px, calc((100% - 6px) - 2em));
|
||||
}
|
||||
}
|
||||
|
||||
.navds-modal__overlay {
|
||||
.navds-modal::backdrop {
|
||||
/*
|
||||
* Cannot use --a-surface-backdrop because ::backdrop does not inherit from anything but itself.
|
||||
* But consumers can do `::backdrop { --ac-modal-backdrop: white; }`
|
||||
* We can add ::backdrop to tokens.css when we remove the polyfill.
|
||||
*/
|
||||
background: var(--ac-modal-backdrop, rgb(2 20 49 / 0.49));
|
||||
animation: akselModalBackdropFadeIn 0.35s cubic-bezier(0.15, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.navds-modal--polyfilled + .backdrop /* Cannot be combined with ::backdrop selector */ {
|
||||
background: var(--ac-modal-backdrop, rgb(2 20 49 / 0.49));
|
||||
|
||||
/* From polyfill: */
|
||||
position: fixed;
|
||||
z-index: var(--a-z-index-modal);
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: var(--ac-modal-backdrop, var(--a-surface-backdrop));
|
||||
padding: var(--a-spacing-4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.navds-modal__button--shake {
|
||||
transform: rotate(0deg);
|
||||
transition: transform 100ms cubic-bezier(0.82, 2, 1, 0.3);
|
||||
}
|
||||
|
||||
/* prettier-ignore */
|
||||
.navds-modal__overlay:active > .navds-modal:not(:active) > .navds-modal__button--shake {
|
||||
transform: rotate(10deg);
|
||||
transition: transform 100ms cubic-bezier(0.82, -2, 1, 0.3);
|
||||
}
|
||||
|
||||
.navds-modal__button {
|
||||
position: absolute;
|
||||
top: var(--a-spacing-4);
|
||||
right: var(--a-spacing-4);
|
||||
display: flex;
|
||||
padding: var(--a-spacing-2);
|
||||
margin-left: var(--a-spacing-4);
|
||||
float: right;
|
||||
}
|
||||
|
||||
.navds-modal__button svg {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
.navds-modal__header {
|
||||
padding: var(--a-spacing-6) var(--a-spacing-6) var(--a-spacing-4) var(--a-spacing-6);
|
||||
}
|
||||
|
||||
.navds-modal__header-icon svg {
|
||||
display: inline;
|
||||
vertical-align: -0.25rem;
|
||||
margin-right: var(--a-spacing-1);
|
||||
}
|
||||
|
||||
.navds-modal__label {
|
||||
font-weight: 600;
|
||||
color: var(--a-text-subtle);
|
||||
}
|
||||
|
||||
.navds-modal__body {
|
||||
padding: var(--a-spacing-6);
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.navds-modal__header + .navds-modal__body {
|
||||
padding-top: var(--a-spacing-0);
|
||||
}
|
||||
|
||||
.navds-modal__footer {
|
||||
display: flex;
|
||||
flex-flow: row-reverse wrap;
|
||||
gap: var(--a-spacing-4);
|
||||
padding: var(--a-spacing-4) var(--a-spacing-6) var(--a-spacing-6) var(--a-spacing-6);
|
||||
}
|
||||
|
||||
.navds-modal__footer :nth-of-type(2) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@keyframes akselModalFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, calc(5% + 4px), 0);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes akselModalBackdropFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "4.12.1",
|
||||
"version": "5.0.2",
|
||||
"description": "CSS for NAV Designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
@@ -27,7 +27,7 @@
|
||||
"css:get-version": "node config/get-version.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^4.12.1",
|
||||
"@navikt/ds-tokens": "^5.0.2",
|
||||
"cssnano": "6.0.0",
|
||||
"fast-glob": "3.2.11",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
.navds-table__header-cell,
|
||||
.navds-table__data-cell {
|
||||
display: table-cell;
|
||||
padding: var(--a-spacing-4) var(--a-spacing-3);
|
||||
padding: var(--a-spacing-3) var(--a-spacing-2);
|
||||
border-bottom: 1px solid var(--ac-table-row-border, var(--a-border-default));
|
||||
text-align: left;
|
||||
}
|
||||
@@ -74,9 +74,14 @@
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.navds-table--large .navds-table__header-cell,
|
||||
.navds-table--large .navds-table__data-cell {
|
||||
padding: var(--a-spacing-4) var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-table--small .navds-table__header-cell,
|
||||
.navds-table--small .navds-table__data-cell {
|
||||
padding: var(--a-spacing-2) var(--a-spacing-3);
|
||||
padding: var(--a-spacing-1) var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-table .navds-checkbox .navds-checkbox__input {
|
||||
@@ -154,7 +159,7 @@
|
||||
transition: border-bottom-color 190ms cubic-bezier(0.6, 0.04, 0.98, 0.335);
|
||||
}
|
||||
|
||||
.navds-table__expandable-row:not(.navds-table__expandable-row--expansion-disabled):hover {
|
||||
.navds-table__expandable-row.navds-table__expandable-row--clickable:not(.navds-table__expandable-row--expansion-disabled):hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -171,55 +176,73 @@
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-cell {
|
||||
padding: 0;
|
||||
width: 3.5rem;
|
||||
padding: 0 var(--a-spacing-2);
|
||||
position: relative;
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
.navds-table--large .navds-table__toggle-expand-cell {
|
||||
padding: 0 var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-table--small .navds-table__toggle-expand-cell {
|
||||
padding: 0;
|
||||
width: 2.25rem;
|
||||
padding: var(--a-spacing-1) var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-button {
|
||||
all: unset;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
cursor: pointer;
|
||||
padding: var(--a-spacing-4);
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: var(--a-border-radius-medium);
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.navds-table--small .navds-table__toggle-expand-button {
|
||||
padding: var(--a-spacing-2);
|
||||
.navds-table__expandable-icon {
|
||||
transition: transform 150ms ease-in-out;
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-button:hover,
|
||||
.navds-table__expandable-row--clickable:hover .navds-table__toggle-expand-button {
|
||||
background-color: var(--a-surface-hover);
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-cell--open > :where(.navds-table__toggle-expand-button) :where(.navds-table__expandable-icon) {
|
||||
transform: translateY(0) rotate(180deg);
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-button:hover > :where(.navds-table__expandable-icon) {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-cell--open > .navds-table__toggle-expand-button:hover > :where(.navds-table__expandable-icon) {
|
||||
transform: translateY(-1px) rotate(180deg);
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-button:focus-visible {
|
||||
box-shadow: inset var(--a-shadow-focus);
|
||||
outline: none;
|
||||
box-shadow: var(--a-shadow-focus);
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-table__toggle-expand-button:focus {
|
||||
box-shadow: inset var(--a-shadow-focus);
|
||||
outline: none;
|
||||
box-shadow: var(--a-shadow-focus);
|
||||
}
|
||||
}
|
||||
|
||||
.navds-table__expandable-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.navds-table--small .navds-table__expandable-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-button:hover .navds-table__expandable-icon {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-cell--open .navds-table__expandable-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.navds-table__toggle-expand-cell--open .navds-table__toggle-expand-button:hover .navds-table__expandable-icon {
|
||||
transform: translateY(-1px) rotate(180deg);
|
||||
.navds-table__toggle-expand-button::after {
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navds-table__expanded-row-cell {
|
||||
@@ -235,9 +258,9 @@
|
||||
}
|
||||
|
||||
.navds-table__expanded-row-content {
|
||||
padding: var(--a-spacing-4) 4.25rem;
|
||||
padding: var(--a-spacing-4) calc(var(--a-spacing-2) + 3rem);
|
||||
}
|
||||
|
||||
.navds-table--small .navds-table__expanded-row-content {
|
||||
padding: var(--a-spacing-2) var(--a-spacing-12);
|
||||
padding: var(--a-spacing-2) calc(var(--a-spacing-2) + 3rem);
|
||||
}
|
||||
@@ -196,7 +196,10 @@
|
||||
},
|
||||
"modal": {
|
||||
"--ac-modal-bg": "--a-surface-default",
|
||||
"--ac-modal-backdrop": "--a-surface-backdrop"
|
||||
"--ac-modal-border": "--a-border-subtle",
|
||||
"--ac-modal-backdrop": "--a-surface-backdrop",
|
||||
"--ac-modal-width-small": "450px",
|
||||
"--ac-modal-width-medium": "700px"
|
||||
},
|
||||
"pagination": {
|
||||
"--ac-pagination-text": "--a-text-default",
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
# @navikt/ds-react
|
||||
|
||||
## 5.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :label: Modal: Bedre type for `width` ([#2191](https://github.com/navikt/aksel/pull/2191))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/aksel-icons@5.0.2
|
||||
|
||||
## 5.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/aksel-icons@5.0.1
|
||||
|
||||
## 5.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- Oppdatert Modal - Se [Migrering](https://aksel.nav.no/grunnleggende/kode/migrering) ([#2135](https://github.com/navikt/aksel/pull/2135))
|
||||
|
||||
- :sparkles: Støtte for header og footer
|
||||
- :boom: Bruker nå native dialog i stedet for react-modal (med polyfill for eldre nettlesere)
|
||||
- :boom: Provider: `appElement` er fjernet
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Table: ExpandableRow har oppdatert knapp for å matche Accordion og ExpansionCard ([#2178](https://github.com/navikt/aksel/pull/2178))
|
||||
|
||||
- Table: Har lagt til ny size: 'large'. ([#2178](https://github.com/navikt/aksel/pull/2178))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/aksel-icons@5.0.0
|
||||
|
||||
## 4.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "4.12.1",
|
||||
"version": "5.0.2",
|
||||
"description": "Aksel react-components for NAV designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -38,13 +38,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "0.24.1",
|
||||
"@navikt/aksel-icons": "^4.12.1",
|
||||
"@navikt/aksel-icons": "^5.0.2",
|
||||
"@radix-ui/react-tabs": "1.0.0",
|
||||
"@radix-ui/react-toggle-group": "1.0.0",
|
||||
"clsx": "^1.2.1",
|
||||
"date-fns": "2.29.3",
|
||||
"react-day-picker": "8.3.4",
|
||||
"react-modal": "3.15.1"
|
||||
"react-day-picker": "8.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "8.13.0",
|
||||
@@ -53,7 +52,6 @@
|
||||
"@testing-library/user-event": "^14.2.0",
|
||||
"@types/faker": "5.5.8",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/react-modal": "^3.13.1",
|
||||
"concurrently": "7.2.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
"faker": "5.5.3",
|
||||
|
||||
@@ -109,9 +109,9 @@ export interface DatePickerDefaultProps
|
||||
onOpenToggle?: () => void;
|
||||
/**
|
||||
* Avoid using if possible!
|
||||
* Changes what CSS position property to use
|
||||
* Changes what CSS position property to use.
|
||||
* You want to use "fixed" if parent wrapper has position relative, but you want popover to escape
|
||||
* @default "absolute"
|
||||
* @default See Popover
|
||||
*/
|
||||
strategy?: "absolute" | "fixed";
|
||||
/**
|
||||
@@ -184,7 +184,7 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
||||
open: _open,
|
||||
onClose,
|
||||
onOpenToggle,
|
||||
strategy = "absolute",
|
||||
strategy,
|
||||
bubbleEscape = false,
|
||||
...rest
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useCallback, useEffect, RefObject } from "react";
|
||||
|
||||
export const useEscape = (
|
||||
open: boolean,
|
||||
setOpen: (openState: boolean) => void,
|
||||
focusRef: any
|
||||
focusRef: RefObject<HTMLElement>
|
||||
) => {
|
||||
const handleClose = useCallback(() => {
|
||||
setOpen(false);
|
||||
@@ -11,7 +11,12 @@ export const useEscape = (
|
||||
}, [focusRef, setOpen]);
|
||||
|
||||
const escape = useCallback(
|
||||
(e) => open && e.key === "Escape" && handleClose(),
|
||||
(event: KeyboardEvent) => {
|
||||
if (open && event.key === "Escape") {
|
||||
event.preventDefault(); // This prevents modal from closing when using datepicker inside modal
|
||||
handleClose();
|
||||
}
|
||||
},
|
||||
[handleClose, open]
|
||||
);
|
||||
|
||||
|
||||
@@ -1,76 +1,91 @@
|
||||
import React, { forwardRef, useEffect, useMemo, useRef } from "react";
|
||||
import React, {
|
||||
forwardRef,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from "react";
|
||||
import cl from "clsx";
|
||||
import ReactModal from "react-modal";
|
||||
import dialogPolyfill from "./dialog-polyfill";
|
||||
import { Detail, Heading, mergeRefs, useId } from "..";
|
||||
import ModalBody from "./ModalBody";
|
||||
import ModalHeader from "./ModalHeader";
|
||||
import ModalFooter from "./ModalFooter";
|
||||
import { getCloseHandler, useBodyScrollLock } from "./ModalUtils";
|
||||
import { ModalContext } from "./ModalContext";
|
||||
|
||||
import { Button, mergeRefs, useProvider } from "..";
|
||||
import ModalContent, { ModalContentType } from "./ModalContent";
|
||||
import { XMarkIcon } from "@navikt/aksel-icons";
|
||||
const needPolyfill =
|
||||
typeof window !== "undefined" && window.HTMLDialogElement === undefined;
|
||||
|
||||
export interface ModalProps {
|
||||
export interface ModalProps
|
||||
extends React.DialogHTMLAttributes<HTMLDialogElement> {
|
||||
/**
|
||||
* Content for the header. Alteratively you can use <Modal.Header> instead for more control,
|
||||
* but then you have to set `aria-label` or `aria-labelledby` on the modal manually.
|
||||
*/
|
||||
header?: {
|
||||
label?: string;
|
||||
icon?: React.ReactNode;
|
||||
heading: string;
|
||||
/**
|
||||
* Heading size
|
||||
* @default "medium"
|
||||
* */
|
||||
size?: "medium" | "small";
|
||||
/**
|
||||
* Removes close-button (X) when false
|
||||
* @default true
|
||||
*/
|
||||
closeButton?: boolean;
|
||||
};
|
||||
/**
|
||||
* Modal content
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Open state for modal
|
||||
* Whether the modal should be visible or not.
|
||||
* Remember to use the `onClose` callback to keep your local state in sync.
|
||||
* You can also use `ref.current.openModal()` and `ref.current.close()`.
|
||||
*/
|
||||
open: boolean;
|
||||
open?: boolean;
|
||||
/**
|
||||
* Callback for modal wanting to close
|
||||
* Called when the modal has been closed
|
||||
*/
|
||||
onClose: () => void;
|
||||
onClose?: React.ReactEventHandler<HTMLDialogElement>;
|
||||
/**
|
||||
* If modal should close on overlay click (click outside Modal)
|
||||
* @default true
|
||||
* Called when the user wants to close the modal (clicked the close button or pressed Esc).
|
||||
* @returns Whether to close the modal
|
||||
*/
|
||||
shouldCloseOnOverlayClick?: boolean;
|
||||
onBeforeClose?: () => boolean | void;
|
||||
/**
|
||||
* Called when the user presses the Esc key, unless `onBeforeClose()` returns `false`.
|
||||
*/
|
||||
onCancel?: React.ReactEventHandler<HTMLDialogElement>;
|
||||
/**
|
||||
* @default fit-content (up to 700px)
|
||||
* */
|
||||
width?: "medium" | "small" | number | `${number}${string}`;
|
||||
/**
|
||||
* User defined classname for modal
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* User defined classname for modal
|
||||
* Sets aria-labelledby on modal.
|
||||
* No need to set this manually if the `header` prop is used. A reference to `header.heading` will be created automatically.
|
||||
* @warning If not using `header`, you should set either `aria-labelledby` or `aria-label`.
|
||||
*/
|
||||
overlayClassName?: string;
|
||||
/**
|
||||
* Removes close-button(X) when false
|
||||
* @default true
|
||||
*/
|
||||
closeButton?: boolean;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
shouldCloseOnEsc?: boolean;
|
||||
/**
|
||||
* Allows custom styling of ReactModal, in accordance with their typing
|
||||
*/
|
||||
style?: ReactModal.Styles;
|
||||
/**
|
||||
* Callback for setting parent element modal will attach to
|
||||
*/
|
||||
parentSelector?(): HTMLElement;
|
||||
"aria-labelledby"?: string;
|
||||
"aria-describedby"?: string;
|
||||
"aria-modal"?: boolean;
|
||||
/**
|
||||
* Sets aria-label on modal
|
||||
* @warning This should be set if not using 'aria-labelledby' or 'aria-describedby'
|
||||
*/
|
||||
"aria-label"?: string;
|
||||
}
|
||||
|
||||
interface ModalComponent
|
||||
extends ModalLifecycle,
|
||||
React.ForwardRefExoticComponent<
|
||||
ModalProps & React.RefAttributes<ReactModal>
|
||||
> {
|
||||
Content: ModalContentType;
|
||||
extends React.ForwardRefExoticComponent<
|
||||
ModalProps & React.RefAttributes<HTMLDialogElement>
|
||||
> {
|
||||
Header: typeof ModalHeader;
|
||||
Body: typeof ModalBody;
|
||||
Footer: typeof ModalFooter;
|
||||
}
|
||||
|
||||
type ModalLifecycle = {
|
||||
setAppElement: (element: any) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* A component that displays a modal dialog.
|
||||
*
|
||||
@@ -78,112 +93,147 @@ type ModalLifecycle = {
|
||||
* @see 🏷️ {@link ModalProps}
|
||||
*
|
||||
* @example
|
||||
* State change with `useRef`
|
||||
* ```jsx
|
||||
* const ref = useRef<HTMLDialogElement>(null);
|
||||
* <Button onClick={() => ref.current?.showModal()}>Open modal</Button>
|
||||
* <Modal
|
||||
* ref={ref}
|
||||
* header={{
|
||||
* label: "Optional label",
|
||||
* icon: <FileIcon aria-hidden />,
|
||||
* heading: "My heading",
|
||||
* }}
|
||||
* >
|
||||
* <Modal.Body>
|
||||
* <BodyLong>Hello world</BodyLong>
|
||||
* </Modal.Body>
|
||||
* <Modal.Footer>
|
||||
* <Button>Save</Button>
|
||||
* <Button type="button" variant="tertiary" onClick={() => ref.current?.close()}>Close</Button>
|
||||
* </Modal.Footer>
|
||||
* </Modal>
|
||||
* ```
|
||||
* @example
|
||||
* State change with `useState`
|
||||
* ```jsx
|
||||
* const [open, setOpen] = useState(false);
|
||||
*
|
||||
* <Modal
|
||||
* open={open}
|
||||
* aria-label="Modal demo"
|
||||
* onClose={() => setOpen((x) => !x)}
|
||||
* onClose={() => setOpen(false)}
|
||||
* aria-labelledby="modal-heading"
|
||||
* >
|
||||
* <Modal.Content>
|
||||
* <Heading spacing level="1" size="large" id="modal-heading">
|
||||
* Viktig info
|
||||
* </Heading>
|
||||
* <BodyLong spacing>
|
||||
* Hallo!
|
||||
* </BodyLong>
|
||||
* </Modal.Content>
|
||||
* <Modal.Header>
|
||||
* <Heading level="1" size="large" id="modal-heading">My heading</Heading>
|
||||
* </Modal.Header>
|
||||
* <Modal.Body>
|
||||
* <BodyLong>Hello world</BodyLong>
|
||||
* </Modal.Body>
|
||||
* </Modal>
|
||||
* ```
|
||||
*/
|
||||
export const Modal = forwardRef<ReactModal, ModalProps>(
|
||||
export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
|
||||
(
|
||||
{
|
||||
header,
|
||||
children,
|
||||
open,
|
||||
onClose,
|
||||
onBeforeClose,
|
||||
onCancel,
|
||||
width,
|
||||
className,
|
||||
overlayClassName,
|
||||
shouldCloseOnOverlayClick = true,
|
||||
shouldCloseOnEsc = true,
|
||||
closeButton = true,
|
||||
"aria-describedby": ariaDescribedBy,
|
||||
"aria-labelledby": ariaLabelledBy,
|
||||
"aria-modal": ariaModal,
|
||||
"aria-label": contentLabel,
|
||||
"aria-labelledby": ariaLabelledby,
|
||||
style,
|
||||
parentSelector,
|
||||
...rest
|
||||
},
|
||||
}: ModalProps,
|
||||
ref
|
||||
) => {
|
||||
const modalRef = useRef<ReactModal | null>(null);
|
||||
const modalRef = useRef<HTMLDialogElement>(null);
|
||||
const mergedRef = useMemo(() => mergeRefs([modalRef, ref]), [ref]);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const rootElement = useProvider()?.rootElement;
|
||||
const appElement = useProvider()?.appElement;
|
||||
const ariaLabelId = useId();
|
||||
|
||||
if (useContext(ModalContext)) {
|
||||
console.error("Modals should not be nested");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
appElement && Modal.setAppElement(appElement);
|
||||
}, [appElement]);
|
||||
|
||||
const onModalCloseRequest = (e) => {
|
||||
if (shouldCloseOnOverlayClick || e.type === "keydown") {
|
||||
onClose();
|
||||
} else if (buttonRef.current) {
|
||||
buttonRef.current.focus();
|
||||
if (needPolyfill && modalRef.current) {
|
||||
dialogPolyfill.registerDialog(modalRef.current);
|
||||
}
|
||||
};
|
||||
}, [modalRef]);
|
||||
|
||||
const getParentSelector = () => {
|
||||
if (parentSelector) {
|
||||
return parentSelector;
|
||||
useEffect(() => {
|
||||
// We need to have this in a useEffect so that the content renders before the modal is displayed,
|
||||
// and in case `open` is true initially.
|
||||
if (modalRef.current && open !== undefined) {
|
||||
if (open && !modalRef.current.open) {
|
||||
modalRef.current.showModal();
|
||||
} else if (!open && modalRef.current.open) {
|
||||
modalRef.current.close();
|
||||
}
|
||||
}
|
||||
return rootElement !== undefined
|
||||
? () => rootElement as HTMLElement
|
||||
: undefined;
|
||||
};
|
||||
}, [modalRef, open]);
|
||||
|
||||
useBodyScrollLock(modalRef, "navds-modal__document-body");
|
||||
|
||||
const isWidthPreset =
|
||||
typeof width === "string" && ["small", "medium"].includes(width);
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
{...rest}
|
||||
parentSelector={getParentSelector()}
|
||||
style={style}
|
||||
isOpen={open}
|
||||
<dialog
|
||||
ref={mergedRef}
|
||||
className={cl("navds-modal", className)}
|
||||
overlayClassName={cl("navds-modal__overlay", overlayClassName)}
|
||||
shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
|
||||
shouldCloseOnEsc={shouldCloseOnEsc}
|
||||
onRequestClose={(e) => onModalCloseRequest(e)}
|
||||
aria={{
|
||||
describedby: ariaDescribedBy,
|
||||
labelledby: ariaLabelledBy,
|
||||
modal: ariaModal,
|
||||
className={cl("navds-modal", className, {
|
||||
"navds-modal--polyfilled": needPolyfill,
|
||||
"navds-modal--autowidth": !width,
|
||||
[`navds-modal--${width}`]: isWidthPreset,
|
||||
})}
|
||||
style={{
|
||||
...style,
|
||||
...(!isWidthPreset ? { width } : {}),
|
||||
}}
|
||||
contentLabel={contentLabel}
|
||||
onCancel={(event) => {
|
||||
// FYI: onCancel fires when you press Esc
|
||||
if (onBeforeClose && onBeforeClose() === false) {
|
||||
event.preventDefault();
|
||||
} else if (onCancel) onCancel(event);
|
||||
}}
|
||||
aria-labelledby={
|
||||
!ariaLabelledby && !rest["aria-label"] && header
|
||||
? ariaLabelId
|
||||
: ariaLabelledby
|
||||
}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
{closeButton && (
|
||||
<Button
|
||||
className={cl("navds-modal__button", {
|
||||
"navds-modal__button--shake": shouldCloseOnOverlayClick,
|
||||
})}
|
||||
size="small"
|
||||
variant="tertiary-neutral"
|
||||
ref={buttonRef}
|
||||
onClick={onClose}
|
||||
icon={<XMarkIcon title="Lukk modalvindu" />}
|
||||
/>
|
||||
)}
|
||||
</ReactModal>
|
||||
<ModalContext.Provider
|
||||
value={{
|
||||
closeHandler: getCloseHandler(modalRef, header, onBeforeClose),
|
||||
}}
|
||||
>
|
||||
{header && (
|
||||
<ModalHeader>
|
||||
{header.label && (
|
||||
<Detail className="navds-modal__label">{header.label}</Detail>
|
||||
)}
|
||||
<Heading
|
||||
size={header.size ?? "medium"}
|
||||
level="1"
|
||||
id={ariaLabelId}
|
||||
>
|
||||
<span className="navds-modal__header-icon">{header.icon}</span>
|
||||
{header.heading}
|
||||
</Heading>
|
||||
</ModalHeader>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</ModalContext.Provider>
|
||||
</dialog>
|
||||
);
|
||||
}
|
||||
) as ModalComponent;
|
||||
|
||||
Modal.setAppElement = (element) => ReactModal.setAppElement(element);
|
||||
Modal.Content = ModalContent;
|
||||
Modal.Header = ModalHeader;
|
||||
Modal.Body = ModalBody;
|
||||
Modal.Footer = ModalFooter;
|
||||
|
||||
export default Modal;
|
||||
@@ -0,0 +1,14 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "clsx";
|
||||
|
||||
export interface ModalBodyProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ModalBody = forwardRef<HTMLDivElement, ModalBodyProps>(
|
||||
({ className, ...rest }, ref) => (
|
||||
<div {...rest} ref={ref} className={cl("navds-modal__body", className)} />
|
||||
)
|
||||
);
|
||||
|
||||
export default ModalBody;
|
||||
@@ -1,26 +0,0 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "clsx";
|
||||
|
||||
export interface ModalContentProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Modal.Content content
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export type ModalContentType = React.ForwardRefExoticComponent<
|
||||
ModalContentProps & React.RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
const ModalContent: ModalContentType = forwardRef(
|
||||
({ className, ...rest }, ref) => (
|
||||
<div
|
||||
{...rest}
|
||||
ref={ref}
|
||||
className={cl("navds-modal__content", className)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
export default ModalContent;
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
interface ModalContextProps {
|
||||
closeHandler?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
export const ModalContext = React.createContext<ModalContextProps | null>(null);
|
||||
@@ -0,0 +1,14 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "clsx";
|
||||
|
||||
export interface ModalFooterProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ModalFooter = forwardRef<HTMLDivElement, ModalFooterProps>(
|
||||
({ className, ...rest }, ref) => (
|
||||
<div {...rest} ref={ref} className={cl("navds-modal__footer", className)} />
|
||||
)
|
||||
);
|
||||
|
||||
export default ModalFooter;
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import cl from "clsx";
|
||||
import { XMarkIcon } from "@navikt/aksel-icons";
|
||||
import { Button } from "../button";
|
||||
import { ModalContext } from "./ModalContext";
|
||||
|
||||
export interface ModalHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Removes close-button (X) when false
|
||||
* @default true
|
||||
*/
|
||||
closeButton?: boolean;
|
||||
}
|
||||
|
||||
const ModalHeader = forwardRef<HTMLDivElement, ModalHeaderProps>(
|
||||
({ children, className, closeButton = true, ...rest }, ref) => {
|
||||
const context = useContext(ModalContext);
|
||||
if (context === null) {
|
||||
console.error("<Modal.Header> has to be used within a <Modal>");
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...rest} ref={ref} className={cl("navds-modal__header", className)}>
|
||||
{context.closeHandler && closeButton && (
|
||||
<Button
|
||||
type="button"
|
||||
className="navds-modal__button"
|
||||
size="small"
|
||||
variant="tertiary-neutral"
|
||||
onClick={context.closeHandler}
|
||||
icon={<XMarkIcon title="Lukk modalvindu" />}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default ModalHeader;
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import type { ModalProps } from "./Modal";
|
||||
|
||||
export function getCloseHandler(
|
||||
modalRef: React.RefObject<HTMLDialogElement>,
|
||||
header: ModalProps["header"],
|
||||
onBeforeClose: ModalProps["onBeforeClose"]
|
||||
) {
|
||||
if (header && header.closeButton === false) return undefined;
|
||||
if (onBeforeClose) {
|
||||
return () => onBeforeClose() !== false && modalRef.current?.close();
|
||||
}
|
||||
return () => modalRef.current?.close();
|
||||
}
|
||||
|
||||
export function useBodyScrollLock(
|
||||
modalRef: React.RefObject<HTMLDialogElement>,
|
||||
bodyClass: string
|
||||
) {
|
||||
React.useEffect(() => {
|
||||
if (!modalRef.current) return;
|
||||
if (modalRef.current.open) document.body.classList.add(bodyClass); // In case `open` is true initially
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
if (modalRef.current?.open) document.body.classList.add(bodyClass);
|
||||
else document.body.classList.remove(bodyClass);
|
||||
});
|
||||
observer.observe(modalRef.current, {
|
||||
attributes: true,
|
||||
attributeFilter: ["open"],
|
||||
});
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
document.body.classList.remove(bodyClass); // In case modal is unmounted before it's closed
|
||||
};
|
||||
}, [modalRef, bodyClass]);
|
||||
}
|
||||
@@ -0,0 +1,980 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
/**
|
||||
* Dispatches the passed event to both an "on<type>" handler as well as via the
|
||||
* normal dispatch operation. Does not bubble.
|
||||
*
|
||||
* @param {!EventTarget} target
|
||||
* @param {!Event} event
|
||||
* @return {boolean}
|
||||
*/
|
||||
function safeDispatchEvent(target, event) {
|
||||
var check = "on" + event.type.toLowerCase();
|
||||
if (typeof target[check] === "function") {
|
||||
target[check](event);
|
||||
}
|
||||
return target.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} el to check for stacking context
|
||||
* @return {boolean} whether this el or its parents creates a stacking context
|
||||
*/
|
||||
function createsStackingContext(el) {
|
||||
while (el && el !== document.body) {
|
||||
var s = window.getComputedStyle(el);
|
||||
var invalid = function (k, ok) {
|
||||
return !(s[k] === undefined || s[k] === ok);
|
||||
};
|
||||
|
||||
if (
|
||||
s.opacity < 1 ||
|
||||
invalid("zIndex", "auto") ||
|
||||
invalid("transform", "none") ||
|
||||
invalid("mixBlendMode", "normal") ||
|
||||
invalid("filter", "none") ||
|
||||
invalid("perspective", "none") ||
|
||||
s["isolation"] === "isolate" ||
|
||||
s.position === "fixed" ||
|
||||
s.webkitOverflowScrolling === "touch"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
el = el.parentElement;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the nearest <dialog> from the passed element.
|
||||
*
|
||||
* @param {Element} el to search from
|
||||
* @return {HTMLDialogElement} dialog found
|
||||
*/
|
||||
function findNearestDialog(el) {
|
||||
while (el) {
|
||||
if (el.localName === "dialog") {
|
||||
return /** @type {HTMLDialogElement} */ el;
|
||||
}
|
||||
if (el.parentElement) {
|
||||
el = el.parentElement;
|
||||
} else if (el.parentNode) {
|
||||
el = el.parentNode.host;
|
||||
} else {
|
||||
el = null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur the specified element, as long as it's not the HTML body element.
|
||||
* This works around an IE9/10 bug - blurring the body causes Windows to
|
||||
* blur the whole application.
|
||||
*
|
||||
* @param {Element} el to blur
|
||||
*/
|
||||
function safeBlur(el) {
|
||||
// Find the actual focused element when the active element is inside a shadow root
|
||||
while (el && el.shadowRoot && el.shadowRoot.activeElement) {
|
||||
el = el.shadowRoot.activeElement;
|
||||
}
|
||||
|
||||
if (el && el.blur && el !== document.body) {
|
||||
el.blur();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!NodeList} nodeList to search
|
||||
* @param {Node} node to find
|
||||
* @return {boolean} whether node is inside nodeList
|
||||
*/
|
||||
function inNodeList(nodeList, node) {
|
||||
for (var i = 0; i < nodeList.length; ++i) {
|
||||
if (nodeList[i] === node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLFormElement} el to check
|
||||
* @return {boolean} whether this form has method="dialog"
|
||||
*/
|
||||
function isFormMethodDialog(el) {
|
||||
if (!el || !el.hasAttribute("method")) {
|
||||
return false;
|
||||
}
|
||||
return el.getAttribute("method").toLowerCase() === "dialog";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!DocumentFragment|!Element} hostElement
|
||||
* @return {?Element}
|
||||
*/
|
||||
function findFocusableElementWithin(hostElement) {
|
||||
// Note that this is 'any focusable area'. This list is probably not exhaustive, but the
|
||||
// alternative involves stepping through and trying to focus everything.
|
||||
var opts = ["button", "input", "keygen", "select", "textarea"];
|
||||
var query = opts.map(function (el) {
|
||||
return el + ":not([disabled])";
|
||||
});
|
||||
// TODO(samthor): tabindex values that are not numeric are not focusable.
|
||||
query.push('[tabindex]:not([disabled]):not([tabindex=""])'); // tabindex != "", not disabled
|
||||
var target = hostElement.querySelector(query.join(", "));
|
||||
|
||||
if (!target && "attachShadow" in Element.prototype) {
|
||||
// If we haven't found a focusable target, see if the host element contains an element
|
||||
// which has a shadowRoot.
|
||||
// Recursively search for the first focusable item in shadow roots.
|
||||
var elems = hostElement.querySelectorAll("*");
|
||||
for (var i = 0; i < elems.length; i++) {
|
||||
if (elems[i].tagName && elems[i].shadowRoot) {
|
||||
target = findFocusableElementWithin(elems[i].shadowRoot);
|
||||
if (target) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an element is attached to the DOM.
|
||||
* @param {Element} element to check
|
||||
* @return {boolean} whether the element is in DOM
|
||||
*/
|
||||
function isConnected(element) {
|
||||
return element.isConnected || document.body.contains(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Event} event
|
||||
* @return {?Element}
|
||||
*/
|
||||
function findFormSubmitter(event) {
|
||||
if (event.submitter) {
|
||||
return event.submitter;
|
||||
}
|
||||
|
||||
var form = event.target;
|
||||
if (!(form instanceof HTMLFormElement)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var submitter = dialogPolyfill.formSubmitter;
|
||||
if (!submitter) {
|
||||
var target = event.target;
|
||||
var root = ("getRootNode" in target && target.getRootNode()) || document;
|
||||
submitter = root.activeElement;
|
||||
}
|
||||
|
||||
if (!submitter || submitter.form !== form) {
|
||||
return null;
|
||||
}
|
||||
return submitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Event} event
|
||||
*/
|
||||
function maybeHandleSubmit(event) {
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
var form = /** @type {!HTMLFormElement} */ event.target;
|
||||
|
||||
// We'd have a value if we clicked on an imagemap.
|
||||
var value = dialogPolyfill.imagemapUseValue;
|
||||
var submitter = findFormSubmitter(event);
|
||||
if (value === null && submitter) {
|
||||
value = submitter.value;
|
||||
}
|
||||
|
||||
// There should always be a dialog as this handler is added specifically on them, but check just
|
||||
// in case.
|
||||
var dialog = findNearestDialog(form);
|
||||
if (!dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prefer formmethod on the button.
|
||||
var formmethod =
|
||||
(submitter && submitter.getAttribute("formmethod")) ||
|
||||
form.getAttribute("method");
|
||||
if (formmethod !== "dialog") {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
if (value != null) {
|
||||
// nb. we explicitly check against null/undefined
|
||||
dialog.close(value);
|
||||
} else {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!HTMLDialogElement} dialog to upgrade
|
||||
* @constructor
|
||||
*/
|
||||
function dialogPolyfillInfo(dialog) {
|
||||
this.dialog_ = dialog;
|
||||
this.replacedStyleTop_ = false;
|
||||
this.openAsModal_ = false;
|
||||
|
||||
// Set a11y role. Browsers that support dialog implicitly know this already.
|
||||
if (!dialog.hasAttribute("role")) {
|
||||
dialog.setAttribute("role", "dialog");
|
||||
}
|
||||
|
||||
dialog.show = this.show.bind(this);
|
||||
dialog.showModal = this.showModal.bind(this);
|
||||
dialog.close = this.close.bind(this);
|
||||
|
||||
dialog.addEventListener("submit", maybeHandleSubmit, false);
|
||||
|
||||
if (!("returnValue" in dialog)) {
|
||||
dialog.returnValue = "";
|
||||
}
|
||||
|
||||
if ("MutationObserver" in window) {
|
||||
var mo = new MutationObserver(this.maybeHideModal.bind(this));
|
||||
mo.observe(dialog, { attributes: true, attributeFilter: ["open"] });
|
||||
} else {
|
||||
// IE10 and below support. Note that DOMNodeRemoved etc fire _before_ removal. They also
|
||||
// seem to fire even if the element was removed as part of a parent removal. Use the removed
|
||||
// events to force downgrade (useful if removed/immediately added).
|
||||
var removed = false;
|
||||
var cb = function () {
|
||||
removed ? this.downgradeModal() : this.maybeHideModal();
|
||||
removed = false;
|
||||
}.bind(this);
|
||||
var timeout;
|
||||
var delayModel = function (ev) {
|
||||
if (ev.target !== dialog) {
|
||||
return;
|
||||
} // not for a child element
|
||||
var cand = "DOMNodeRemoved";
|
||||
removed |= ev.type.substr(0, cand.length) === cand;
|
||||
window.clearTimeout(timeout);
|
||||
timeout = window.setTimeout(cb, 0);
|
||||
};
|
||||
["DOMAttrModified", "DOMNodeRemoved", "DOMNodeRemovedFromDocument"].forEach(
|
||||
function (name) {
|
||||
dialog.addEventListener(name, delayModel);
|
||||
}
|
||||
);
|
||||
}
|
||||
// Note that the DOM is observed inside DialogManager while any dialog
|
||||
// is being displayed as a modal, to catch modal removal from the DOM.
|
||||
|
||||
Object.defineProperty(dialog, "open", {
|
||||
set: this.setOpen.bind(this),
|
||||
get: dialog.hasAttribute.bind(dialog, "open"),
|
||||
});
|
||||
|
||||
this.backdrop_ = document.createElement("div");
|
||||
this.backdrop_.className = "backdrop";
|
||||
this.backdrop_.addEventListener(
|
||||
"mouseup",
|
||||
this.backdropMouseEvent_.bind(this)
|
||||
);
|
||||
this.backdrop_.addEventListener(
|
||||
"mousedown",
|
||||
this.backdropMouseEvent_.bind(this)
|
||||
);
|
||||
this.backdrop_.addEventListener("click", this.backdropMouseEvent_.bind(this));
|
||||
}
|
||||
|
||||
dialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ {
|
||||
get dialog() {
|
||||
return this.dialog_;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe remove this dialog from the modal top layer. This is called when
|
||||
* a modal dialog may no longer be tenable, e.g., when the dialog is no
|
||||
* longer open or is no longer part of the DOM.
|
||||
*/
|
||||
maybeHideModal: function () {
|
||||
if (this.dialog_.hasAttribute("open") && isConnected(this.dialog_)) {
|
||||
return;
|
||||
}
|
||||
this.downgradeModal();
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove this dialog from the modal top layer, leaving it as a non-modal.
|
||||
*/
|
||||
downgradeModal: function () {
|
||||
if (!this.openAsModal_) {
|
||||
return;
|
||||
}
|
||||
this.openAsModal_ = false;
|
||||
this.dialog_.style.zIndex = "";
|
||||
|
||||
// This won't match the native <dialog> exactly because if the user set top on a centered
|
||||
// polyfill dialog, that top gets thrown away when the dialog is closed. Not sure it's
|
||||
// possible to polyfill this perfectly.
|
||||
if (this.replacedStyleTop_) {
|
||||
this.dialog_.style.top = "";
|
||||
this.replacedStyleTop_ = false;
|
||||
}
|
||||
|
||||
// Clear the backdrop and remove from the manager.
|
||||
this.backdrop_.parentNode &&
|
||||
this.backdrop_.parentNode.removeChild(this.backdrop_);
|
||||
dialogPolyfill.dm.removeDialog(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {boolean} value whether to open or close this dialog
|
||||
*/
|
||||
setOpen: function (value) {
|
||||
if (value) {
|
||||
this.dialog_.hasAttribute("open") ||
|
||||
this.dialog_.setAttribute("open", "");
|
||||
} else {
|
||||
this.dialog_.removeAttribute("open");
|
||||
this.maybeHideModal(); // nb. redundant with MutationObserver
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles mouse events ('mouseup', 'mousedown', 'click') on the fake .backdrop element, redirecting them as if
|
||||
* they were on the dialog itself.
|
||||
*
|
||||
* @param {!Event} e to redirect
|
||||
*/
|
||||
backdropMouseEvent_: function (e) {
|
||||
if (!this.dialog_.hasAttribute("tabindex")) {
|
||||
// Clicking on the backdrop should move the implicit cursor, even if dialog cannot be
|
||||
// focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this
|
||||
// would not be needed - clicks would move the implicit cursor there.
|
||||
var fake = document.createElement("div");
|
||||
this.dialog_.insertBefore(fake, this.dialog_.firstChild);
|
||||
fake.tabIndex = -1;
|
||||
fake.focus();
|
||||
this.dialog_.removeChild(fake);
|
||||
} else {
|
||||
this.dialog_.focus();
|
||||
}
|
||||
|
||||
var redirectedEvent = document.createEvent("MouseEvents");
|
||||
redirectedEvent.initMouseEvent(
|
||||
e.type,
|
||||
e.bubbles,
|
||||
e.cancelable,
|
||||
window,
|
||||
e.detail,
|
||||
e.screenX,
|
||||
e.screenY,
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
e.ctrlKey,
|
||||
e.altKey,
|
||||
e.shiftKey,
|
||||
e.metaKey,
|
||||
e.button,
|
||||
e.relatedTarget
|
||||
);
|
||||
this.dialog_.dispatchEvent(redirectedEvent);
|
||||
e.stopPropagation();
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses on the first focusable element within the dialog. This will always blur the current
|
||||
* focus, even if nothing within the dialog is found.
|
||||
*/
|
||||
focus_: function () {
|
||||
// Find element with `autofocus` attribute, or fall back to the first form/tabindex control.
|
||||
var target = this.dialog_.querySelector("[autofocus]:not([disabled])");
|
||||
if (!target && this.dialog_.tabIndex >= 0) {
|
||||
target = this.dialog_;
|
||||
}
|
||||
if (!target) {
|
||||
target = findFocusableElementWithin(this.dialog_);
|
||||
}
|
||||
safeBlur(document.activeElement);
|
||||
target && target.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the zIndex for the backdrop and dialog.
|
||||
*
|
||||
* @param {number} dialogZ
|
||||
* @param {number} backdropZ
|
||||
*/
|
||||
updateZIndex: function (dialogZ, backdropZ) {
|
||||
if (dialogZ < backdropZ) {
|
||||
throw new Error("dialogZ should never be < backdropZ");
|
||||
}
|
||||
this.dialog_.style.zIndex = dialogZ;
|
||||
this.backdrop_.style.zIndex = backdropZ;
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the dialog. If the dialog is already open, this does nothing.
|
||||
*/
|
||||
show: function () {
|
||||
if (!this.dialog_.open) {
|
||||
this.setOpen(true);
|
||||
this.focus_();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show this dialog modally.
|
||||
*/
|
||||
showModal: function () {
|
||||
if (this.dialog_.hasAttribute("open")) {
|
||||
throw new Error(
|
||||
"Failed to execute 'showModal' on dialog: The element is already open, and therefore cannot be opened modally."
|
||||
);
|
||||
}
|
||||
if (!isConnected(this.dialog_)) {
|
||||
throw new Error(
|
||||
"Failed to execute 'showModal' on dialog: The element is not in a Document."
|
||||
);
|
||||
}
|
||||
if (!dialogPolyfill.dm.pushDialog(this)) {
|
||||
throw new Error(
|
||||
"Failed to execute 'showModal' on dialog: There are too many open modal dialogs."
|
||||
);
|
||||
}
|
||||
|
||||
if (createsStackingContext(this.dialog_.parentElement)) {
|
||||
console.warn(
|
||||
"A dialog is being shown inside a stacking context. " +
|
||||
"This may cause it to be unusable. For more information, see this link: " +
|
||||
"https://github.com/GoogleChrome/dialog-polyfill/#stacking-context"
|
||||
);
|
||||
}
|
||||
|
||||
this.setOpen(true);
|
||||
this.openAsModal_ = true;
|
||||
|
||||
// Optionally center vertically, relative to the current viewport.
|
||||
if (dialogPolyfill.needsCentering(this.dialog_)) {
|
||||
dialogPolyfill.reposition(this.dialog_);
|
||||
this.replacedStyleTop_ = true;
|
||||
} else {
|
||||
this.replacedStyleTop_ = false;
|
||||
}
|
||||
|
||||
// Insert backdrop.
|
||||
this.dialog_.parentNode.insertBefore(
|
||||
this.backdrop_,
|
||||
this.dialog_.nextSibling
|
||||
);
|
||||
|
||||
// Focus on whatever inside the dialog.
|
||||
this.focus_();
|
||||
},
|
||||
|
||||
/**
|
||||
* Closes this HTMLDialogElement. This is optional vs clearing the open
|
||||
* attribute, however this fires a 'close' event.
|
||||
*
|
||||
* @param {string=} opt_returnValue to use as the returnValue
|
||||
*/
|
||||
close: function (opt_returnValue) {
|
||||
if (!this.dialog_.hasAttribute("open")) {
|
||||
throw new Error(
|
||||
"Failed to execute 'close' on dialog: The element does not have an 'open' attribute, and therefore cannot be closed."
|
||||
);
|
||||
}
|
||||
this.setOpen(false);
|
||||
|
||||
// Leave returnValue untouched in case it was set directly on the element
|
||||
if (opt_returnValue !== undefined) {
|
||||
this.dialog_.returnValue = opt_returnValue;
|
||||
}
|
||||
|
||||
// Triggering "close" event for any attached listeners on the <dialog>.
|
||||
var closeEvent = new window.CustomEvent("close", {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
});
|
||||
safeDispatchEvent(this.dialog_, closeEvent);
|
||||
},
|
||||
};
|
||||
|
||||
var dialogPolyfill = {};
|
||||
|
||||
dialogPolyfill.reposition = function (element) {
|
||||
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
|
||||
var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;
|
||||
element.style.top = Math.max(scrollTop, topValue) + "px";
|
||||
};
|
||||
|
||||
dialogPolyfill.isInlinePositionSetByStylesheet = function (element) {
|
||||
for (var i = 0; i < document.styleSheets.length; ++i) {
|
||||
var styleSheet = document.styleSheets[i];
|
||||
var cssRules = null;
|
||||
// Some browsers throw on cssRules.
|
||||
try {
|
||||
cssRules = styleSheet.cssRules;
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
if (!cssRules) {
|
||||
continue;
|
||||
}
|
||||
for (var j = 0; j < cssRules.length; ++j) {
|
||||
var rule = cssRules[j];
|
||||
var selectedNodes = null;
|
||||
// Ignore errors on invalid selector texts.
|
||||
try {
|
||||
selectedNodes = document.querySelectorAll(rule.selectorText);
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
if (!selectedNodes || !inNodeList(selectedNodes, element)) {
|
||||
continue;
|
||||
}
|
||||
var cssTop = rule.style.getPropertyValue("top");
|
||||
var cssBottom = rule.style.getPropertyValue("bottom");
|
||||
if (
|
||||
(cssTop && cssTop !== "auto") ||
|
||||
(cssBottom && cssBottom !== "auto")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
dialogPolyfill.needsCentering = function (dialog) {
|
||||
var computedStyle = window.getComputedStyle(dialog);
|
||||
if (computedStyle.position !== "absolute") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We must determine whether the top/bottom specified value is non-auto. In
|
||||
// WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but
|
||||
// Firefox returns the used value. So we do this crazy thing instead: check
|
||||
// the inline style and then go through CSS rules.
|
||||
if (
|
||||
(dialog.style.top !== "auto" && dialog.style.top !== "") ||
|
||||
(dialog.style.bottom !== "auto" && dialog.style.bottom !== "")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Element} element to force upgrade
|
||||
*/
|
||||
dialogPolyfill.forceRegisterDialog = function (element) {
|
||||
if (window.HTMLDialogElement || element.showModal) {
|
||||
console.warn(
|
||||
"This browser already supports <dialog>, the polyfill " +
|
||||
"may not work correctly",
|
||||
element
|
||||
);
|
||||
}
|
||||
if (element.localName !== "dialog") {
|
||||
throw new Error("Failed to register dialog: The element is not a dialog.");
|
||||
}
|
||||
new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ element);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Element} element to upgrade, if necessary
|
||||
*/
|
||||
dialogPolyfill.registerDialog = function (element) {
|
||||
if (!element.showModal) {
|
||||
dialogPolyfill.forceRegisterDialog(element);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
dialogPolyfill.DialogManager = function () {
|
||||
/** @type {!Array<!dialogPolyfillInfo>} */
|
||||
this.pendingDialogStack = [];
|
||||
|
||||
var checkDOM = this.checkDOM_.bind(this);
|
||||
|
||||
// The overlay is used to simulate how a modal dialog blocks the document.
|
||||
// The blocking dialog is positioned on top of the overlay, and the rest of
|
||||
// the dialogs on the pending dialog stack are positioned below it. In the
|
||||
// actual implementation, the modal dialog stacking is controlled by the
|
||||
// top layer, where z-index has no effect.
|
||||
this.overlay = document.createElement("div");
|
||||
this.overlay.className = "_dialog_overlay";
|
||||
this.overlay.addEventListener(
|
||||
"click",
|
||||
function (e) {
|
||||
this.forwardTab_ = undefined;
|
||||
e.stopPropagation();
|
||||
checkDOM([]); // sanity-check DOM
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.handleKey_ = this.handleKey_.bind(this);
|
||||
this.handleFocus_ = this.handleFocus_.bind(this);
|
||||
|
||||
this.zIndexLow_ = 100000;
|
||||
this.zIndexHigh_ = 100000 + 150;
|
||||
|
||||
this.forwardTab_ = undefined;
|
||||
|
||||
if ("MutationObserver" in window) {
|
||||
this.mo_ = new MutationObserver(function (records) {
|
||||
var removed = [];
|
||||
records.forEach(function (rec) {
|
||||
for (var i = 0, c; (c = rec.removedNodes[i]); ++i) {
|
||||
if (!(c instanceof Element)) {
|
||||
continue;
|
||||
} else if (c.localName === "dialog") {
|
||||
removed.push(c);
|
||||
}
|
||||
removed = removed.concat(c.querySelectorAll("dialog"));
|
||||
}
|
||||
});
|
||||
removed.length && checkDOM(removed);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called on the first modal dialog being shown. Adds the overlay and related
|
||||
* handlers.
|
||||
*/
|
||||
dialogPolyfill.DialogManager.prototype.blockDocument = function () {
|
||||
document.documentElement.addEventListener("focus", this.handleFocus_, true);
|
||||
document.addEventListener("keydown", this.handleKey_);
|
||||
this.mo_ && this.mo_.observe(document, { childList: true, subtree: true });
|
||||
};
|
||||
|
||||
/**
|
||||
* Called on the first modal dialog being removed, i.e., when no more modal
|
||||
* dialogs are visible.
|
||||
*/
|
||||
dialogPolyfill.DialogManager.prototype.unblockDocument = function () {
|
||||
document.documentElement.removeEventListener(
|
||||
"focus",
|
||||
this.handleFocus_,
|
||||
true
|
||||
);
|
||||
document.removeEventListener("keydown", this.handleKey_);
|
||||
this.mo_ && this.mo_.disconnect();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the stacking of all known dialogs.
|
||||
*/
|
||||
dialogPolyfill.DialogManager.prototype.updateStacking = function () {
|
||||
var zIndex = this.zIndexHigh_;
|
||||
|
||||
for (var i = 0, dpi; (dpi = this.pendingDialogStack[i]); ++i) {
|
||||
dpi.updateZIndex(--zIndex, --zIndex);
|
||||
if (i === 0) {
|
||||
this.overlay.style.zIndex = --zIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the overlay a sibling of the dialog itself.
|
||||
var last = this.pendingDialogStack[0];
|
||||
if (last) {
|
||||
var p = last.dialog.parentNode || document.body;
|
||||
p.appendChild(this.overlay);
|
||||
} else if (this.overlay.parentNode) {
|
||||
this.overlay.parentNode.removeChild(this.overlay);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Element} candidate to check if contained or is the top-most modal dialog
|
||||
* @return {boolean} whether candidate is contained in top dialog
|
||||
*/
|
||||
dialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function (
|
||||
candidate
|
||||
) {
|
||||
while ((candidate = findNearestDialog(candidate))) {
|
||||
for (var i = 0, dpi; (dpi = this.pendingDialogStack[i]); ++i) {
|
||||
if (dpi.dialog === candidate) {
|
||||
return i === 0; // only valid if top-most
|
||||
}
|
||||
}
|
||||
candidate = candidate.parentElement;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.handleFocus_ = function (event) {
|
||||
var target = event.composedPath ? event.composedPath()[0] : event.target;
|
||||
|
||||
if (this.containedByTopDialog_(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.activeElement === document.documentElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
safeBlur(/** @type {Element} */ target);
|
||||
|
||||
if (this.forwardTab_ === undefined) {
|
||||
return;
|
||||
} // move focus only from a tab key
|
||||
|
||||
var dpi = this.pendingDialogStack[0];
|
||||
var dialog = dpi.dialog;
|
||||
var position = dialog.compareDocumentPosition(target);
|
||||
if (position & Node.DOCUMENT_POSITION_PRECEDING) {
|
||||
if (this.forwardTab_) {
|
||||
// forward
|
||||
dpi.focus_();
|
||||
} else if (target !== document.documentElement) {
|
||||
// backwards if we're not already focused on <html>
|
||||
document.documentElement.focus();
|
||||
}
|
||||
} else {
|
||||
// TODO: Focus after the dialog, is ignored.
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.handleKey_ = function (event) {
|
||||
this.forwardTab_ = undefined;
|
||||
if (event.keyCode === 27) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var cancelEvent = new window.CustomEvent("cancel", {
|
||||
bubbles: false,
|
||||
cancelable: true,
|
||||
});
|
||||
var dpi = this.pendingDialogStack[0];
|
||||
if (dpi && safeDispatchEvent(dpi.dialog, cancelEvent)) {
|
||||
dpi.dialog.close();
|
||||
}
|
||||
} else if (event.keyCode === 9) {
|
||||
this.forwardTab_ = !event.shiftKey;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds and downgrades any known modal dialogs that are no longer displayed. Dialogs that are
|
||||
* removed and immediately readded don't stay modal, they become normal.
|
||||
*
|
||||
* @param {!Array<!HTMLDialogElement>} removed that have definitely been removed
|
||||
*/
|
||||
dialogPolyfill.DialogManager.prototype.checkDOM_ = function (removed) {
|
||||
// This operates on a clone because it may cause it to change. Each change also calls
|
||||
// updateStacking, which only actually needs to happen once. But who removes many modal dialogs
|
||||
// at a time?!
|
||||
var clone = this.pendingDialogStack.slice();
|
||||
clone.forEach(function (dpi) {
|
||||
if (removed.indexOf(dpi.dialog) !== -1) {
|
||||
dpi.downgradeModal();
|
||||
} else {
|
||||
dpi.maybeHideModal();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!dialogPolyfillInfo} dpi
|
||||
* @return {boolean} whether the dialog was allowed
|
||||
*/
|
||||
dialogPolyfill.DialogManager.prototype.pushDialog = function (dpi) {
|
||||
var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1;
|
||||
if (this.pendingDialogStack.length >= allowed) {
|
||||
return false;
|
||||
}
|
||||
if (this.pendingDialogStack.unshift(dpi) === 1) {
|
||||
this.blockDocument();
|
||||
}
|
||||
this.updateStacking();
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!dialogPolyfillInfo} dpi
|
||||
*/
|
||||
dialogPolyfill.DialogManager.prototype.removeDialog = function (dpi) {
|
||||
var index = this.pendingDialogStack.indexOf(dpi);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pendingDialogStack.splice(index, 1);
|
||||
if (this.pendingDialogStack.length === 0) {
|
||||
this.unblockDocument();
|
||||
}
|
||||
this.updateStacking();
|
||||
};
|
||||
|
||||
if (typeof window !== "undefined" && window.HTMLDialogElement === undefined) {
|
||||
dialogPolyfill.dm = new dialogPolyfill.DialogManager();
|
||||
dialogPolyfill.formSubmitter = null;
|
||||
dialogPolyfill.imagemapUseValue = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs global handlers, such as click listers and native method overrides. These are needed
|
||||
* even if a no dialog is registered, as they deal with <form method="dialog">.
|
||||
*/
|
||||
if (typeof window !== "undefined" && window.HTMLDialogElement === undefined) {
|
||||
/**
|
||||
* If HTMLFormElement translates method="DIALOG" into 'get', then replace the descriptor with
|
||||
* one that returns the correct value.
|
||||
*/
|
||||
var testForm = document.createElement("form");
|
||||
testForm.setAttribute("method", "dialog");
|
||||
if (testForm.method !== "dialog") {
|
||||
var methodDescriptor = Object.getOwnPropertyDescriptor(
|
||||
HTMLFormElement.prototype,
|
||||
"method"
|
||||
);
|
||||
if (methodDescriptor) {
|
||||
// nb. Some older iOS and older PhantomJS fail to return the descriptor. Don't do anything
|
||||
// and don't bother to update the element.
|
||||
var realGet = methodDescriptor.get;
|
||||
methodDescriptor.get = function () {
|
||||
if (isFormMethodDialog(this)) {
|
||||
return "dialog";
|
||||
}
|
||||
return realGet.call(this);
|
||||
};
|
||||
var realSet = methodDescriptor.set;
|
||||
/** @this {HTMLElement} */
|
||||
methodDescriptor.set = function (v) {
|
||||
if (typeof v === "string" && v.toLowerCase() === "dialog") {
|
||||
return this.setAttribute("method", v);
|
||||
}
|
||||
return realSet.call(this, v);
|
||||
};
|
||||
Object.defineProperty(
|
||||
HTMLFormElement.prototype,
|
||||
"method",
|
||||
methodDescriptor
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global 'click' handler, to capture the <input type="submit"> or <button> element which has
|
||||
* submitted a <form method="dialog">. Needed as Safari and others don't report this inside
|
||||
* document.activeElement.
|
||||
*/
|
||||
document.addEventListener(
|
||||
"click",
|
||||
function (ev) {
|
||||
dialogPolyfill.formSubmitter = null;
|
||||
dialogPolyfill.imagemapUseValue = null;
|
||||
if (ev.defaultPrevented) {
|
||||
return;
|
||||
} // e.g. a submit which prevents default submission
|
||||
|
||||
var target = /** @type {Element} */ ev.target;
|
||||
if ("composedPath" in ev) {
|
||||
var path = ev.composedPath();
|
||||
target = path.shift() || target;
|
||||
}
|
||||
if (!target || !isFormMethodDialog(target.form)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var valid =
|
||||
target.type === "submit" &&
|
||||
["button", "input"].indexOf(target.localName) > -1;
|
||||
if (!valid) {
|
||||
if (!(target.localName === "input" && target.type === "image")) {
|
||||
return;
|
||||
}
|
||||
// this is a <input type="image">, which can submit forms
|
||||
dialogPolyfill.imagemapUseValue = ev.offsetX + "," + ev.offsetY;
|
||||
}
|
||||
|
||||
var dialog = findNearestDialog(target);
|
||||
if (!dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
dialogPolyfill.formSubmitter = target;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
/**
|
||||
* Global 'submit' handler. This handles submits of `method="dialog"` which are invalid, i.e.,
|
||||
* outside a dialog. They get prevented.
|
||||
*/
|
||||
document.addEventListener("submit", function (ev) {
|
||||
var form = ev.target;
|
||||
var dialog = findNearestDialog(form);
|
||||
if (dialog) {
|
||||
return; // ignore, handle there
|
||||
}
|
||||
|
||||
var submitter = findFormSubmitter(ev);
|
||||
var formmethod =
|
||||
(submitter && submitter.getAttribute("formmethod")) ||
|
||||
form.getAttribute("method");
|
||||
if (formmethod === "dialog") {
|
||||
ev.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Replace the native HTMLFormElement.submit() method, as it won't fire the
|
||||
* submit event and give us a chance to respond.
|
||||
*/
|
||||
var nativeFormSubmit = HTMLFormElement.prototype.submit;
|
||||
var replacementFormSubmit = function () {
|
||||
if (!isFormMethodDialog(this)) {
|
||||
return nativeFormSubmit.call(this);
|
||||
}
|
||||
var dialog = findNearestDialog(this);
|
||||
dialog && dialog.close();
|
||||
};
|
||||
HTMLFormElement.prototype.submit = replacementFormSubmit;
|
||||
}
|
||||
|
||||
export default dialogPolyfill as {
|
||||
registerDialog(dialog: HTMLDialogElement): void;
|
||||
forceRegisterDialog(dialog: HTMLDialogElement): void;
|
||||
};
|
||||
@@ -1,2 +1,4 @@
|
||||
export { default as Modal, type ModalProps } from "./Modal";
|
||||
export { type ModalContentProps } from "./ModalContent";
|
||||
export { type ModalHeaderProps } from "./ModalHeader";
|
||||
export { type ModalBodyProps } from "./ModalBody";
|
||||
export { type ModalFooterProps } from "./ModalFooter";
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { FileIcon } from "@navikt/aksel-icons";
|
||||
import { BodyLong, Button, Heading } from "..";
|
||||
import Modal from "./Modal";
|
||||
|
||||
@@ -7,78 +7,161 @@ export default {
|
||||
title: "ds-react/Modal",
|
||||
component: Modal,
|
||||
parameters: {
|
||||
chromatic: { delay: 1000 },
|
||||
chromatic: { pauseAnimationAtEnd: true },
|
||||
},
|
||||
};
|
||||
|
||||
export const Default = {
|
||||
render: (props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
Modal.setAppElement?.("#root");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>Open Modal</Button>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="header123"
|
||||
{...props}
|
||||
>
|
||||
<Modal.Content>
|
||||
<Heading spacing id="header123" level="1" size="large">
|
||||
Header
|
||||
</Heading>
|
||||
<Heading spacing level="2" size="medium">
|
||||
Header
|
||||
</Heading>
|
||||
<BodyLong>
|
||||
Voluptate laboris mollit dolore qui. Magna elit.
|
||||
</BodyLong>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
args: {
|
||||
shouldCloseOnOverlayClick: true,
|
||||
closeButton: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const ParentSelector = () => {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
Modal.setAppElement?.("#root");
|
||||
}, []);
|
||||
|
||||
const parentEl = document.getElementById("custom-container");
|
||||
export const WithUseRef = () => {
|
||||
const ref = useRef<HTMLDialogElement>(null);
|
||||
const ref2 = useRef<HTMLDialogElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id="custom-container" />
|
||||
<Button onClick={() => setOpen(true)}>Open Modal</Button>
|
||||
<Button onClick={() => ref.current?.showModal()}>Open Modal</Button>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="header123"
|
||||
parentSelector={parentEl ? () => parentEl : undefined}
|
||||
open={ref.current ? undefined : true /* initially open */}
|
||||
ref={ref}
|
||||
header={{
|
||||
label: "Optional label",
|
||||
icon: <FileIcon aria-hidden />,
|
||||
heading: "Title",
|
||||
size: "small",
|
||||
}}
|
||||
>
|
||||
<Modal.Content>
|
||||
<Heading spacing id="header123" level="1" size="large">
|
||||
Header
|
||||
</Heading>
|
||||
<Heading spacing level="2" size="medium">
|
||||
Header
|
||||
</Heading>
|
||||
<BodyLong>Voluptate laboris mollit dolore qui. Magna elit.</BodyLong>
|
||||
</Modal.Content>
|
||||
<Modal.Body>
|
||||
<BodyLong spacing>
|
||||
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.
|
||||
</BodyLong>
|
||||
|
||||
{/* Nested modal */}
|
||||
<Modal
|
||||
ref={ref2}
|
||||
onBeforeClose={() =>
|
||||
window.confirm("Are you sure you want to close the modal?")
|
||||
}
|
||||
aria-labelledby="heading123"
|
||||
>
|
||||
<Modal.Header>
|
||||
<Heading size="medium" level="2" id="heading123">
|
||||
Custom header
|
||||
</Heading>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
Nesting modals is not recommended, but works in most cases.
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button onClick={() => ref2.current?.close()}>
|
||||
Close w/o confirm
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
<BodyLong>
|
||||
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.
|
||||
</BodyLong>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button>Primary</Button>
|
||||
<Button variant="secondary" onClick={() => ref2.current?.showModal()}>
|
||||
Secondary
|
||||
</Button>
|
||||
<Button variant="tertiary">Tertiary</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
WithUseRef.storyName = "With useRef";
|
||||
|
||||
export const WithUseState = () => {
|
||||
const [open, setOpen] = useState(true);
|
||||
const [open2, setOpen2] = useState(false);
|
||||
|
||||
return (
|
||||
<div style={{ minHeight: "1000px", display: "flex", alignItems: "center" }}>
|
||||
<div style={{ display: "flex", gap: "1em" }}>
|
||||
<Button variant="secondary">Button</Button>
|
||||
<Button onClick={() => setOpen(true)}>Open Modal</Button>
|
||||
<Button variant="secondary">Button</Button>
|
||||
</div>
|
||||
<Modal
|
||||
header={{ heading: "Simple header" }}
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
width={400}
|
||||
>
|
||||
<Modal.Body>
|
||||
This modal has width set to 400.
|
||||
{/* Nested modal */}
|
||||
<Modal
|
||||
open={open2}
|
||||
onClose={(e) => {
|
||||
e.stopPropagation(); // onClose wil propagate to parent modal if not stopped
|
||||
setOpen2(false);
|
||||
}}
|
||||
aria-label="Nested modal"
|
||||
width={800}
|
||||
>
|
||||
<Modal.Body>
|
||||
<BodyLong spacing>
|
||||
Nesting modals is not recommended, but works in most cases. This
|
||||
one does not have header or footer, but is bigger than the
|
||||
parent modal.
|
||||
</BodyLong>
|
||||
<BodyLong spacing>Width is set to 800.</BodyLong>
|
||||
<Button onClick={() => setOpen2(false)}>Close</Button>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button onClick={() => setOpen2(true)}>Primary</Button>
|
||||
<Button variant="secondary" onClick={() => setOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
WithUseState.storyName = "With useState";
|
||||
|
||||
export const EmptyHeader = () => (
|
||||
<div style={{ minWidth: "800px", minHeight: "600px" } /* For Chromatic */}>
|
||||
<Modal open>
|
||||
<Modal.Header />
|
||||
<Modal.Body>
|
||||
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.
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const Small = () => (
|
||||
<Modal open width="small" header={{ heading: "Simple header" }}>
|
||||
<Modal.Body>Lorem ipsum dolor sit amet.</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
export const Medium = () => (
|
||||
<Modal open width="medium" header={{ heading: "Simple header" }}>
|
||||
<Modal.Body>Lorem ipsum dolor sit amet.</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
@@ -16,10 +16,12 @@ import React, {
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
useContext,
|
||||
} from "react";
|
||||
import { mergeRefs } from "..";
|
||||
import { useClientLayoutEffect, useEventListener } from "../util";
|
||||
import PopoverContent, { PopoverContentType } from "./PopoverContent";
|
||||
import { ModalContext } from "../modal/ModalContext";
|
||||
|
||||
export interface PopoverProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
@@ -117,13 +119,15 @@ export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
|
||||
onClose,
|
||||
placement = "top",
|
||||
offset,
|
||||
strategy: userStrategy = "absolute",
|
||||
strategy: userStrategy,
|
||||
bubbleEscape = false,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const arrowRef = useRef<HTMLDivElement | null>(null);
|
||||
const isInModal = useContext(ModalContext) !== null;
|
||||
const chosenStrategy = userStrategy ?? (isInModal ? "fixed" : "absolute");
|
||||
|
||||
const {
|
||||
x,
|
||||
@@ -135,7 +139,7 @@ export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
|
||||
placement: flPlacement,
|
||||
middlewareData: { arrow: { x: arrowX, y: arrowY } = {} },
|
||||
} = useFloating({
|
||||
strategy: userStrategy,
|
||||
strategy: chosenStrategy,
|
||||
placement,
|
||||
open: open,
|
||||
onOpenChange: onClose,
|
||||
|
||||
@@ -2,13 +2,9 @@ import React, { createContext, useContext } from "react";
|
||||
|
||||
export interface ProviderContextType {
|
||||
/**
|
||||
* Global root-element to attach portals to (Modal, Tooltip)
|
||||
* Global root-element to attach portals to (Tooltip)
|
||||
*/
|
||||
rootElement?: HTMLElement;
|
||||
/**
|
||||
* Global config for appElement (Modal)
|
||||
*/
|
||||
appElement?: HTMLElement;
|
||||
}
|
||||
|
||||
export const ProviderContext = createContext<ProviderContextType | undefined>(
|
||||
@@ -18,7 +14,6 @@ export const ProviderContext = createContext<ProviderContextType | undefined>(
|
||||
export interface ProviderProps {
|
||||
children?: React.ReactNode;
|
||||
rootElement?: HTMLElement;
|
||||
appElement?: HTMLElement;
|
||||
}
|
||||
|
||||
export const useProvider = () => useContext(ProviderContext);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "clsx";
|
||||
import { BodyShort } from "..";
|
||||
import { TableContext } from "./Table";
|
||||
|
||||
export interface DataCellProps
|
||||
extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
||||
@@ -19,8 +18,6 @@ export interface DataCellType
|
||||
|
||||
export const DataCell: DataCellType = forwardRef(
|
||||
({ className, children = "", align, ...rest }, ref) => {
|
||||
const context = useContext(TableContext);
|
||||
|
||||
return (
|
||||
<BodyShort
|
||||
as="td"
|
||||
@@ -28,7 +25,6 @@ export const DataCell: DataCellType = forwardRef(
|
||||
className={cl("navds-table__data-cell", className, {
|
||||
[`navds-table__data-cell--align-${align}`]: align,
|
||||
})}
|
||||
size={context?.size}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -96,6 +96,7 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
|
||||
"navds-table__expandable-row--open": isOpen,
|
||||
"navds-table__expandable-row--expansion-disabled":
|
||||
expansionDisabled,
|
||||
"navds-table__expandable-row--clickable": expandOnRowClick,
|
||||
})}
|
||||
onClick={(e) => {
|
||||
!expansionDisabled && expandOnRowClick && onRowClick(e);
|
||||
@@ -110,8 +111,8 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
|
||||
>
|
||||
{!expansionDisabled && (
|
||||
<button
|
||||
type="button"
|
||||
className="navds-table__toggle-expand-button"
|
||||
type="button"
|
||||
aria-controls={id}
|
||||
aria-expanded={isOpen}
|
||||
onClick={expansionHandler}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import cl from "clsx";
|
||||
import { TableContext } from "./Table";
|
||||
import React, { forwardRef } from "react";
|
||||
|
||||
export interface HeaderCellProps
|
||||
extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
||||
@@ -19,14 +18,11 @@ export interface HeaderCellType
|
||||
|
||||
export const HeaderCell: HeaderCellType = forwardRef(
|
||||
({ className, children, align, ...rest }, ref) => {
|
||||
const context = useContext(TableContext);
|
||||
|
||||
return (
|
||||
<th
|
||||
ref={ref}
|
||||
className={cl("navds-table__header-cell", "navds-label", className, {
|
||||
[`navds-table__header-cell--align-${align}`]: align,
|
||||
"navds-label--small": context?.size === "small",
|
||||
})}
|
||||
{...rest}
|
||||
>
|
||||
|
||||
@@ -16,10 +16,10 @@ export interface SortState {
|
||||
export interface TableProps
|
||||
extends React.TableHTMLAttributes<HTMLTableElement> {
|
||||
/**
|
||||
* Changes padding
|
||||
* Changes padding around Cells
|
||||
* @default "medium"
|
||||
*/
|
||||
size?: "medium" | "small";
|
||||
size?: "large" | "medium" | "small";
|
||||
/**
|
||||
* Zebra striped table
|
||||
* @default false
|
||||
@@ -49,7 +49,6 @@ export interface TableType
|
||||
}
|
||||
|
||||
export interface TableContextProps {
|
||||
size: "medium" | "small";
|
||||
onSortChange?: (sortKey: string) => void;
|
||||
sort?: SortState;
|
||||
}
|
||||
@@ -100,7 +99,7 @@ export const Table = forwardRef(
|
||||
},
|
||||
ref
|
||||
) => (
|
||||
<TableContext.Provider value={{ size, onSortChange, sort }}>
|
||||
<TableContext.Provider value={{ onSortChange, sort }}>
|
||||
<table
|
||||
{...rest}
|
||||
ref={ref}
|
||||
|
||||
@@ -36,8 +36,44 @@ export const Expandable = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ExpandableLarge = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Table size="large">
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell />
|
||||
{columns.map(({ key, name }) => (
|
||||
<Table.HeaderCell key={key}>{name}</Table.HeaderCell>
|
||||
))}
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{data.slice(0, 1).map((data) => (
|
||||
<Table.ExpandableRow content={data.content} key={data.name}>
|
||||
{columns.map(({ key }) => (
|
||||
<Table.DataCell key={key}>{data[key]}</Table.DataCell>
|
||||
))}
|
||||
</Table.ExpandableRow>
|
||||
))}
|
||||
{data.slice(1, 2).map((data) => (
|
||||
<Table.ExpandableRow
|
||||
content={data.content}
|
||||
key={data.name}
|
||||
open={open}
|
||||
onOpenChange={(open) => setOpen(open)}
|
||||
>
|
||||
{columns.map(({ key }) => (
|
||||
<Table.DataCell key={key}>{data[key]}</Table.DataCell>
|
||||
))}
|
||||
</Table.ExpandableRow>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExpandableSmall = () => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Table size="small">
|
||||
|
||||
@@ -77,7 +77,10 @@ export const NoShadeOnHover = () => <TableComponent shadeOnHover={false} />;
|
||||
export const ZebraNoShadeOnHover = () => (
|
||||
<TableComponent zebraStripes shadeOnHover={false} />
|
||||
);
|
||||
export const Small = () => <TableComponent size="small" />;
|
||||
export const SizeLarge = () => <TableComponent size="large" />;
|
||||
export const SizeMedium = () => <TableComponent size="medium" />;
|
||||
export const SizeSmall = () => <TableComponent size="small" />;
|
||||
|
||||
export const Buttons = () => <TableComponent size="small" button />;
|
||||
|
||||
export const WithDivs = () => {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @navikt/ds-tailwind
|
||||
|
||||
## 5.0.2
|
||||
|
||||
## 5.0.1
|
||||
|
||||
## 5.0.0
|
||||
|
||||
## 4.12.1
|
||||
|
||||
## 4.12.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tailwind",
|
||||
"version": "4.12.1",
|
||||
"version": "5.0.2",
|
||||
"description": "Tailwind config based on @navikt/ds-tokens",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
@@ -22,7 +22,7 @@
|
||||
"directory": "@navikt/core/tailwind"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^4.12.1",
|
||||
"@navikt/ds-tokens": "^5.0.2",
|
||||
"@types/jest": "^29.0.0",
|
||||
"color": "4.2.3",
|
||||
"jest": "^29.0.0",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @navikt/ds-tokens
|
||||
|
||||
## 5.0.2
|
||||
|
||||
## 5.0.1
|
||||
|
||||
## 5.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- Gray: Gråfarger er nå mindre varme ([#2092](https://github.com/navikt/aksel/pull/2092))
|
||||
|
||||
- Action: Selected-varianter av action er nå 'blue' ('deepblue' før) ([#2092](https://github.com/navikt/aksel/pull/2092))
|
||||
|
||||
## 4.12.1
|
||||
|
||||
## 4.12.0
|
||||
|
||||
@@ -75,28 +75,28 @@
|
||||
{ "name": "--a-nav-red", "value": "rgba(195, 0, 0, 1)" }
|
||||
],
|
||||
"global-gray": [
|
||||
{ "name": "--a-gray-50", "value": "rgba(247, 247, 247, 1)" },
|
||||
{ "name": "--a-gray-100", "value": "rgba(241, 241, 241, 1)" },
|
||||
{ "name": "--a-gray-200", "value": "rgba(229, 229, 229, 1)" },
|
||||
{ "name": "--a-gray-300", "value": "rgba(207, 207, 207, 1)" },
|
||||
{ "name": "--a-gray-400", "value": "rgba(176, 176, 176, 1)" },
|
||||
{ "name": "--a-gray-500", "value": "rgba(143, 143, 143, 1)" },
|
||||
{ "name": "--a-gray-600", "value": "rgba(112, 112, 112, 1)" },
|
||||
{ "name": "--a-gray-700", "value": "rgba(89, 89, 89, 1)" },
|
||||
{ "name": "--a-gray-800", "value": "rgba(64, 64, 64, 1)" },
|
||||
{ "name": "--a-gray-900", "value": "rgba(38, 38, 38, 1)" }
|
||||
{ "name": "--a-gray-50", "value": "rgba(242, 243, 245, 1)" },
|
||||
{ "name": "--a-gray-100", "value": "rgba(236, 238, 240, 1)" },
|
||||
{ "name": "--a-gray-200", "value": "rgba(224, 227, 230, 1)" },
|
||||
{ "name": "--a-gray-300", "value": "rgba(203, 207, 213, 1)" },
|
||||
{ "name": "--a-gray-400", "value": "rgba(170, 176, 186, 1)" },
|
||||
{ "name": "--a-gray-500", "value": "rgba(131, 140, 154, 1)" },
|
||||
{ "name": "--a-gray-600", "value": "rgba(103, 111, 123, 1)" },
|
||||
{ "name": "--a-gray-700", "value": "rgba(82, 89, 98, 1)" },
|
||||
{ "name": "--a-gray-800", "value": "rgba(66, 71, 79, 1)" },
|
||||
{ "name": "--a-gray-900", "value": "rgba(35, 38, 42, 1)" }
|
||||
],
|
||||
"global-grayalpha": [
|
||||
{ "name": "--a-grayalpha-50", "value": "rgba(0, 0, 0, 0.03)" },
|
||||
{ "name": "--a-grayalpha-100", "value": "rgba(0, 0, 0, 0.05)" },
|
||||
{ "name": "--a-grayalpha-200", "value": "rgba(0, 0, 0, 0.1)" },
|
||||
{ "name": "--a-grayalpha-300", "value": "rgba(0, 0, 0, 0.19)" },
|
||||
{ "name": "--a-grayalpha-400", "value": "rgba(0, 0, 0, 0.31)" },
|
||||
{ "name": "--a-grayalpha-500", "value": "rgba(0, 0, 0, 0.44)" },
|
||||
{ "name": "--a-grayalpha-600", "value": "rgba(0, 0, 0, 0.56)" },
|
||||
{ "name": "--a-grayalpha-700", "value": "rgba(0, 0, 0, 0.65)" },
|
||||
{ "name": "--a-grayalpha-800", "value": "rgba(0, 0, 0, 0.75)" },
|
||||
{ "name": "--a-grayalpha-900", "value": "rgba(0, 0, 0, 0.85)" }
|
||||
{ "name": "--a-grayalpha-50", "value": "rgba(38, 55, 89, 0.06)" },
|
||||
{ "name": "--a-grayalpha-100", "value": "rgba(18, 43, 68, 0.08)" },
|
||||
{ "name": "--a-grayalpha-200", "value": "rgba(17, 41, 64, 0.13)" },
|
||||
{ "name": "--a-grayalpha-300", "value": "rgba(7, 26, 54, 0.21)" },
|
||||
{ "name": "--a-grayalpha-400", "value": "rgba(5, 23, 51, 0.34)" },
|
||||
{ "name": "--a-grayalpha-500", "value": "rgba(2, 20, 49, 0.49)" },
|
||||
{ "name": "--a-grayalpha-600", "value": "rgba(2, 15, 34, 0.6)" },
|
||||
{ "name": "--a-grayalpha-700", "value": "rgba(1, 11, 24, 0.68)" },
|
||||
{ "name": "--a-grayalpha-800", "value": "rgba(3, 11, 22, 0.75)" },
|
||||
{ "name": "--a-grayalpha-900", "value": "rgba(2, 5, 9, 0.87)" }
|
||||
],
|
||||
"global-blue": [
|
||||
{ "name": "--a-blue-50", "value": "rgba(230, 240, 255, 1)" },
|
||||
@@ -196,17 +196,17 @@
|
||||
],
|
||||
|
||||
"semantic-border-default": [
|
||||
{ "name": "--a-border-default", "value": "rgba(0, 0, 0, 0.44)" },
|
||||
{ "name": "--a-border-strong", "value": "rgba(0, 0, 0, 0.65)" },
|
||||
{ "name": "--a-border-divider", "value": "rgba(0, 0, 0, 0.19)" },
|
||||
{ "name": "--a-border-subtle", "value": "rgba(0, 0, 0, 0.1)" },
|
||||
{ "name": "--a-border-subtle-hover", "value": "rgba(0, 0, 0, 0.19)" },
|
||||
{ "name": "--a-border-on-inverted", "value": "rgba(229, 229, 229, 1)" },
|
||||
{ "name": "--a-border-on-inverted-subtle", "value": "rgba(89, 89, 89, 1)" }
|
||||
{ "name": "--a-border-default", "value": "rgba(2, 20, 49, 0.49)" },
|
||||
{ "name": "--a-border-strong", "value": "rgba(1, 11, 24, 0.68)" },
|
||||
{ "name": "--a-border-divider", "value": "rgba(7, 26, 54, 0.21)" },
|
||||
{ "name": "--a-border-subtle", "value": "rgba(7, 26, 54, 0.21)" },
|
||||
{ "name": "--a-border-subtle-hover", "value": "rgba(5, 23, 51, 0.34)" },
|
||||
{ "name": "--a-border-on-inverted", "value": "rgba(224, 227, 230, 1)" },
|
||||
{ "name": "--a-border-on-inverted-subtle", "value": "rgba(82, 89, 98, 1)" }
|
||||
],
|
||||
"semantic-border-action": [
|
||||
{ "name": "--a-border-action", "value": "rgba(0, 103, 197, 1)" },
|
||||
{ "name": "--a-border-action-selected", "value": "rgba(0, 91, 130, 1)" },
|
||||
{ "name": "--a-border-action-selected", "value": "rgba(0, 69, 156, 1)" },
|
||||
{ "name": "--a-border-selected", "value": "rgba(0, 103, 197, 1)" }
|
||||
],
|
||||
"semantic-border-status": [
|
||||
@@ -228,19 +228,19 @@
|
||||
{ "name": "--a-border-alt-3", "value": "rgba(0, 91, 130, 1)" }
|
||||
],
|
||||
"semantic-text": [
|
||||
{ "name": "--a-text-default", "value": "rgba(38, 38, 38, 1)" },
|
||||
{ "name": "--a-text-subtle", "value": "rgba(0, 0, 0, 0.65)" },
|
||||
{ "name": "--a-text-default", "value": "rgba(35, 38, 42, 1)" },
|
||||
{ "name": "--a-text-subtle", "value": "rgba(1, 11, 24, 0.68)" },
|
||||
{ "name": "--a-text-visited", "value": "rgba(99, 70, 137, 1)" },
|
||||
{ "name": "--a-text-danger", "value": "rgba(195, 0, 0, 1)" },
|
||||
{ "name": "--a-text-action", "value": "rgba(0, 103, 197, 1)" },
|
||||
{ "name": "--a-text-action-selected", "value": "rgba(0, 91, 130, 1)" },
|
||||
{ "name": "--a-text-action-selected", "value": "rgba(0, 69, 156, 1)" },
|
||||
{
|
||||
"name": "--a-text-action-on-action-subtle",
|
||||
"value": "rgba(0, 86, 180, 1)"
|
||||
},
|
||||
{ "name": "--a-text-on-warning", "value": "rgba(38, 38, 38, 1)" },
|
||||
{ "name": "--a-text-on-info", "value": "rgba(38, 38, 38, 1)" },
|
||||
{ "name": "--a-text-on-alt-2", "value": "rgba(38, 38, 38, 1)" },
|
||||
{ "name": "--a-text-on-warning", "value": "rgba(35, 38, 42, 1)" },
|
||||
{ "name": "--a-text-on-info", "value": "rgba(35, 38, 42, 1)" },
|
||||
{ "name": "--a-text-on-alt-2", "value": "rgba(35, 38, 42, 1)" },
|
||||
{ "name": "--a-text-on-inverted", "value": "rgba(255, 255, 255, 1)" },
|
||||
{ "name": "--a-text-on-neutral", "value": "rgba(255, 255, 255, 1)" },
|
||||
{ "name": "--a-text-on-action", "value": "rgba(255, 255, 255, 1)" },
|
||||
@@ -251,15 +251,15 @@
|
||||
],
|
||||
"semantic-surface-default": [
|
||||
{ "name": "--a-surface-default", "value": "rgba(255, 255, 255, 1)" },
|
||||
{ "name": "--a-surface-hover", "value": "rgba(0, 0, 0, 0.05)" },
|
||||
{ "name": "--a-surface-active", "value": "rgba(0, 0, 0, 0.1)" },
|
||||
{ "name": "--a-surface-hover", "value": "rgba(18, 43, 68, 0.08)" },
|
||||
{ "name": "--a-surface-active", "value": "rgba(17, 41, 64, 0.13)" },
|
||||
{ "name": "--a-surface-selected", "value": "rgba(230, 240, 255, 1)" },
|
||||
{ "name": "--a-surface-subtle", "value": "rgba(247, 247, 247, 1)" },
|
||||
{ "name": "--a-surface-subtle", "value": "rgba(242, 243, 245, 1)" },
|
||||
{ "name": "--a-surface-transparent", "value": "rgba(255, 255, 255, 0)" },
|
||||
{ "name": "--a-surface-backdrop", "value": "rgba(0, 0, 0, 0.65)" },
|
||||
{ "name": "--a-surface-inverted-hover", "value": "rgba(64, 64, 64, 1)" },
|
||||
{ "name": "--a-surface-inverted-active", "value": "rgba(89, 89, 89, 1)" },
|
||||
{ "name": "--a-surface-inverted", "value": "rgba(38, 38, 38, 1)" }
|
||||
{ "name": "--a-surface-backdrop", "value": "rgba(2, 20, 49, 0.49)" },
|
||||
{ "name": "--a-surface-inverted-hover", "value": "rgba(66, 71, 79, 1)" },
|
||||
{ "name": "--a-surface-inverted-active", "value": "rgba(82, 89, 98, 1)" },
|
||||
{ "name": "--a-surface-inverted", "value": "rgba(35, 38, 42, 1)" }
|
||||
],
|
||||
"semantic-surface-action": [
|
||||
{ "name": "--a-surface-action-subtle", "value": "rgba(230, 240, 255, 1)" },
|
||||
@@ -269,24 +269,27 @@
|
||||
},
|
||||
{ "name": "--a-surface-action", "value": "rgba(0, 103, 197, 1)" },
|
||||
{ "name": "--a-surface-action-hover", "value": "rgba(0, 86, 180, 1)" },
|
||||
{ "name": "--a-surface-action-active", "value": "rgba(0, 91, 130, 1)" },
|
||||
{ "name": "--a-surface-action-selected", "value": "rgba(0, 91, 130, 1)" },
|
||||
{ "name": "--a-surface-action-active", "value": "rgba(0, 69, 156, 1)" },
|
||||
{ "name": "--a-surface-action-selected", "value": "rgba(0, 69, 156, 1)" },
|
||||
{
|
||||
"name": "--a-surface-action-selected-hover",
|
||||
"value": "rgba(0, 80, 119, 1)"
|
||||
"value": "rgba(0, 52, 125, 1)"
|
||||
}
|
||||
],
|
||||
"semantic-surface-neutral": [
|
||||
{ "name": "--a-surface-neutral-subtle", "value": "rgba(0, 0, 0, 0.05)" },
|
||||
{ "name": "--a-surface-neutral-subtle", "value": "rgba(18, 43, 68, 0.08)" },
|
||||
{
|
||||
"name": "--a-surface-neutral-subtle-hover",
|
||||
"value": "rgba(0, 0, 0, 0.1)"
|
||||
"value": "rgba(17, 41, 64, 0.13)"
|
||||
},
|
||||
{ "name": "--a-surface-neutral-moderate", "value": "rgba(0, 0, 0, 0.1)" },
|
||||
{ "name": "--a-surface-neutral", "value": "rgba(89, 89, 89, 1)" },
|
||||
{ "name": "--a-surface-neutral-hover", "value": "rgba(64, 64, 64, 1)" },
|
||||
{ "name": "--a-surface-neutral-selected", "value": "rgba(38, 38, 38, 1)" },
|
||||
{ "name": "--a-surface-neutral-active", "value": "rgba(38, 38, 38, 1)" }
|
||||
{
|
||||
"name": "--a-surface-neutral-moderate",
|
||||
"value": "rgba(17, 41, 64, 0.13)"
|
||||
},
|
||||
{ "name": "--a-surface-neutral", "value": "rgba(82, 89, 98, 1)" },
|
||||
{ "name": "--a-surface-neutral-hover", "value": "rgba(66, 71, 79, 1)" },
|
||||
{ "name": "--a-surface-neutral-selected", "value": "rgba(35, 38, 42, 1)" },
|
||||
{ "name": "--a-surface-neutral-active", "value": "rgba(35, 38, 42, 1)" }
|
||||
],
|
||||
"semantic-surface-success": [
|
||||
{ "name": "--a-surface-success-subtle", "value": "rgba(204, 241, 214, 1)" },
|
||||
@@ -356,18 +359,18 @@
|
||||
},
|
||||
{
|
||||
"name": "--a-bg-subtle",
|
||||
"value": "rgba(241, 241, 241, 1)"
|
||||
"value": "rgba(236, 238, 240, 1)"
|
||||
}
|
||||
],
|
||||
"semantic-icon-defaults": [
|
||||
{ "name": "--a-icon-default", "value": "rgba(38, 38, 38, 1)" },
|
||||
{ "name": "--a-icon-subtle", "value": "rgba(0, 0, 0, 0.65)" },
|
||||
{ "name": "--a-icon-default", "value": "rgba(35, 38, 42, 1)" },
|
||||
{ "name": "--a-icon-subtle", "value": "rgba(1, 11, 24, 0.68)" },
|
||||
{ "name": "--a-icon-on-neutral", "value": "rgba(255, 255, 255, 1)" },
|
||||
{ "name": "--a-icon-on-inverted", "value": "rgba(255, 255, 255, 1)" }
|
||||
],
|
||||
"semantic-icon-action": [
|
||||
{ "name": "--a-icon-action", "value": "rgba(0, 103, 197, 1)" },
|
||||
{ "name": "--a-icon-action-selected", "value": "rgba(0, 91, 130, 1)" },
|
||||
{ "name": "--a-icon-action-selected", "value": "rgba(0, 69, 156, 1)" },
|
||||
{
|
||||
"name": "--a-icon-action-on-action-subtle",
|
||||
"value": "rgba(0, 86, 180, 1)"
|
||||
@@ -380,9 +383,9 @@
|
||||
{ "name": "--a-icon-danger", "value": "rgba(195, 0, 0, 1)" },
|
||||
{ "name": "--a-icon-on-danger", "value": "rgba(255, 255, 255, 1)" },
|
||||
{ "name": "--a-icon-warning", "value": "rgba(199, 115, 0, 1)" },
|
||||
{ "name": "--a-icon-on-warning", "value": "rgba(38, 38, 38, 1)" },
|
||||
{ "name": "--a-icon-on-warning", "value": "rgba(35, 38, 42, 1)" },
|
||||
{ "name": "--a-icon-info", "value": "rgba(35, 107, 125, 1)" },
|
||||
{ "name": "--a-icon-on-info", "value": "rgba(38, 38, 38, 1)" }
|
||||
{ "name": "--a-icon-on-info", "value": "rgba(35, 38, 42, 1)" }
|
||||
],
|
||||
"semantic-icon-alt": [
|
||||
{ "name": "--a-icon-alt-1", "value": "rgba(99, 70, 137, 1)" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tokens",
|
||||
"version": "4.12.1",
|
||||
"version": "5.0.2",
|
||||
"description": "Design-tokens for NAV designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
|
||||
@@ -13,66 +13,66 @@
|
||||
},
|
||||
"gray": {
|
||||
"50": {
|
||||
"value": "rgba(247, 247, 247, 1)"
|
||||
"value": "rgba(242, 243, 245, 1)"
|
||||
},
|
||||
"100": {
|
||||
"value": "rgba(241, 241, 241, 1)"
|
||||
"value": "rgba(236, 238, 240, 1)"
|
||||
},
|
||||
"200": {
|
||||
"value": "rgba(229, 229, 229, 1)"
|
||||
"value": "rgba(224, 227, 230, 1)"
|
||||
},
|
||||
"300": {
|
||||
"value": "rgba(207, 207, 207, 1)"
|
||||
"value": "rgba(203, 207, 213, 1)"
|
||||
},
|
||||
"400": {
|
||||
"value": "rgba(176, 176, 176, 1)"
|
||||
"value": "rgba(170, 176, 186, 1)"
|
||||
},
|
||||
"500": {
|
||||
"value": "rgba(143, 143, 143, 1)"
|
||||
"value": "rgba(131, 140, 154, 1)"
|
||||
},
|
||||
"600": {
|
||||
"value": "rgba(112, 112, 112, 1)"
|
||||
"value": "rgba(103, 111, 123, 1)"
|
||||
},
|
||||
"700": {
|
||||
"value": "rgba(89, 89, 89, 1)"
|
||||
"value": "rgba(82, 89, 98, 1)"
|
||||
},
|
||||
"800": {
|
||||
"value": "rgba(64, 64, 64, 1)"
|
||||
"value": "rgba(66, 71, 79, 1)"
|
||||
},
|
||||
"900": {
|
||||
"value": "rgba(38, 38, 38, 1)"
|
||||
"value": "rgba(35, 38, 42, 1)"
|
||||
}
|
||||
},
|
||||
"grayalpha": {
|
||||
"50": {
|
||||
"value": "rgba(0, 0, 0, 0.03)"
|
||||
"value": "rgba(38, 55, 89, 0.06)"
|
||||
},
|
||||
"100": {
|
||||
"value": "rgba(0, 0, 0, 0.05)"
|
||||
"value": "rgba(18, 43, 68, 0.08)"
|
||||
},
|
||||
"200": {
|
||||
"value": "rgba(0, 0, 0, 0.1)"
|
||||
"value": "rgba(17, 41, 64, 0.13)"
|
||||
},
|
||||
"300": {
|
||||
"value": "rgba(0, 0, 0, 0.19)"
|
||||
"value": "rgba(7, 26, 54, 0.21)"
|
||||
},
|
||||
"400": {
|
||||
"value": "rgba(0, 0, 0, 0.31)"
|
||||
"value": "rgba(5, 23, 51, 0.34)"
|
||||
},
|
||||
"500": {
|
||||
"value": "rgba(0, 0, 0, 0.44)"
|
||||
"value": "rgba(2, 20, 49, 0.49)"
|
||||
},
|
||||
"600": {
|
||||
"value": "rgba(0, 0, 0, 0.56)"
|
||||
"value": "rgba(2, 15, 34, 0.6)"
|
||||
},
|
||||
"700": {
|
||||
"value": "rgba(0, 0, 0, 0.65)"
|
||||
"value": "rgba(1, 11, 24, 0.68)"
|
||||
},
|
||||
"800": {
|
||||
"value": "rgba(0, 0, 0, 0.75)"
|
||||
"value": "rgba(3, 11, 22, 0.75)"
|
||||
},
|
||||
"900": {
|
||||
"value": "rgba(0, 0, 0, 0.85)"
|
||||
"value": "rgba(2, 5, 9, 0.87)"
|
||||
}
|
||||
},
|
||||
"blue": {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"action": {
|
||||
"selected": {
|
||||
"value": "{a.deepblue.500.value}"
|
||||
"value": "{a.blue.700.value}"
|
||||
},
|
||||
"on": {
|
||||
"action": {
|
||||
@@ -89,7 +89,7 @@
|
||||
"value": "{a.transparent.value}"
|
||||
},
|
||||
"backdrop": {
|
||||
"value": "{a.grayalpha.700.value}"
|
||||
"value": "{a.grayalpha.500.value}"
|
||||
},
|
||||
"inverted": {
|
||||
"hover": {
|
||||
@@ -115,13 +115,13 @@
|
||||
"value": "{a.blue.600.value}"
|
||||
},
|
||||
"active": {
|
||||
"value": "{a.deepblue.500.value}"
|
||||
"value": "{a.blue.700.value}"
|
||||
},
|
||||
"selected": {
|
||||
"hover": {
|
||||
"value": "{a.deepblue.600.value}"
|
||||
"value": "{a.blue.800.value}"
|
||||
},
|
||||
"@": { "value": "{a.deepblue.500.value}" }
|
||||
"@": { "value": "{a.blue.700.value}" }
|
||||
},
|
||||
"@": {
|
||||
"value": "{a.blue.500.value}"
|
||||
@@ -233,11 +233,11 @@
|
||||
"value": "{a.grayalpha.300.value}"
|
||||
},
|
||||
"subtle": {
|
||||
"hover": { "value": "{a.grayalpha.300.value}" },
|
||||
"@": { "value": "{a.grayalpha.200.value}" }
|
||||
"hover": { "value": "{a.grayalpha.400.value}" },
|
||||
"@": { "value": "{a.grayalpha.300.value}" }
|
||||
},
|
||||
"action": {
|
||||
"selected": { "value": "{a.deepblue.500.value}" },
|
||||
"selected": { "value": "{a.blue.700.value}" },
|
||||
"@": { "value": "{a.blue.500.value}" }
|
||||
},
|
||||
"selected": {
|
||||
@@ -287,7 +287,7 @@
|
||||
"value": "{a.grayalpha.700.value}"
|
||||
},
|
||||
"action": {
|
||||
"selected": { "value": "{a.deepblue.500.value}" },
|
||||
"selected": { "value": "{a.blue.700.value}" },
|
||||
"on": {
|
||||
"action": {
|
||||
"subtle": {
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
## 5.0.2
|
||||
|
||||
### @navikt/ds-react
|
||||
|
||||
- :label: Modal: Bedre type for `width` ([#2191](https://github.com/navikt/aksel/pull/2191))
|
||||
|
||||
## 5.0.1
|
||||
|
||||
### @navikt/ds-css
|
||||
|
||||
- Checkbox: Hotfix da checkmark ikke var sentrert hvis man brukte tailwind ([`90db7dd0c`](https://github.com/navikt/aksel/commit/90db7dd0c120c16a387d3169c05c1f33dc694323))
|
||||
|
||||
## 5.0.0
|
||||
|
||||
### @navikt/ds-tokens
|
||||
|
||||
- Gray: Gråfarger er nå mindre varme ([#2092](https://github.com/navikt/aksel/pull/2092))
|
||||
|
||||
- Action: Selected-varianter av action er nå 'blue' ('deepblue' før) ([#2092](https://github.com/navikt/aksel/pull/2092))
|
||||
|
||||
### @navikt/ds-css
|
||||
|
||||
- Oppdatert Modal - Se [Migrering](https://aksel.nav.no/grunnleggende/kode/migrering) ([#2135](https://github.com/navikt/aksel/pull/2135))
|
||||
|
||||
- :sparkles: Støtte for header og footer
|
||||
- :boom: Bruker nå native dialog i stedet for react-modal (med polyfill for eldre nettlesere)
|
||||
|
||||
<!---->
|
||||
|
||||
- Table: ExpandableRow har oppdatert knapp for å matche Accordion og ExpansionCard ([#2178](https://github.com/navikt/aksel/pull/2178))
|
||||
|
||||
- Table: Alle størrelser har justert padding. Small-size table bruker nå også standard typografi-størrelse (18px). ([#2178](https://github.com/navikt/aksel/pull/2178))
|
||||
|
||||
### @navikt/ds-react
|
||||
|
||||
- Oppdatert Modal - Se [Migrering](https://aksel.nav.no/grunnleggende/kode/migrering) ([#2135](https://github.com/navikt/aksel/pull/2135))
|
||||
|
||||
- :sparkles: Støtte for header og footer
|
||||
- :boom: Bruker nå native dialog i stedet for react-modal (med polyfill for eldre nettlesere)
|
||||
- :boom: Provider: `appElement` er fjernet
|
||||
|
||||
<!---->
|
||||
|
||||
- Table: ExpandableRow har oppdatert knapp for å matche Accordion og ExpansionCard ([#2178](https://github.com/navikt/aksel/pull/2178))
|
||||
|
||||
- Table: Har lagt til ny size: 'large'. ([#2178](https://github.com/navikt/aksel/pull/2178))
|
||||
|
||||
## 4.12.1
|
||||
|
||||
### @navikt/ds-css
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
"website"
|
||||
],
|
||||
"dependencies": {
|
||||
"@navikt/aksel-icons": "^4.12.1",
|
||||
"@navikt/ds-css": "^4.12.1",
|
||||
"@navikt/ds-react": "^4.12.1",
|
||||
"@navikt/ds-tailwind": "^4.12.1",
|
||||
"@navikt/ds-tokens": "^4.12.1"
|
||||
"@navikt/aksel-icons": "^5.0.2",
|
||||
"@navikt/ds-css": "^5.0.2",
|
||||
"@navikt/ds-react": "^5.0.2",
|
||||
"@navikt/ds-tailwind": "^5.0.2",
|
||||
"@navikt/ds-tokens": "^5.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier-plugin-tailwindcss": "^0.2.3"
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { MenuHamburgerIcon, XMarkIcon } from "@navikt/aksel-icons";
|
||||
import { MenuHamburgerIcon } from "@navikt/aksel-icons";
|
||||
|
||||
import { Button } from "@navikt/ds-react";
|
||||
import { Modal } from "@navikt/ds-react";
|
||||
import cl from "clsx";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import ReactModal from "react-modal";
|
||||
import { useState } from "react";
|
||||
import { logNav } from "../..";
|
||||
import styles from "./header.module.css";
|
||||
|
||||
const LinkElement = ({ name, href, onClick }) => {
|
||||
const { asPath } = useRouter();
|
||||
@@ -41,14 +39,9 @@ const LinkElement = ({ name, href, onClick }) => {
|
||||
|
||||
export const Hamburger = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
ReactModal.setAppElement("#__next");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="z-[1050] mr-0 grid h-full place-content-center">
|
||||
<div className="mr-2 grid h-full place-content-center lg:hidden">
|
||||
<button
|
||||
onClick={() => setOpen(!open)}
|
||||
aria-expanded={open}
|
||||
@@ -60,25 +53,16 @@ export const Hamburger = () => {
|
||||
aria-label="Åpne meny"
|
||||
/>
|
||||
</button>
|
||||
<ReactModal
|
||||
isOpen={open}
|
||||
onRequestClose={() => setOpen(false)}
|
||||
aria={{ modal: true }}
|
||||
overlayClassName={styles.modalOverlay}
|
||||
contentLabel="Meny"
|
||||
className="bg-surface-default shadow-xlarge absolute left-4 right-4 top-0 block rounded px-11 py-14 sm:left-auto sm:right-6 sm:w-96 sm:max-w-[90%] lg:hidden"
|
||||
>
|
||||
</div>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
header={{ heading: "Meny" }}
|
||||
width="small"
|
||||
>
|
||||
<Modal.Body>
|
||||
<nav aria-label="hovedmeny">
|
||||
<ul>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={() => setOpen(false)}
|
||||
className={cl(
|
||||
styles.akselTertiaryButton,
|
||||
"absolute right-2 top-2"
|
||||
)}
|
||||
icon={<XMarkIcon title="lukk meny" fontSize="1.5rem" />}
|
||||
/>
|
||||
<LinkElement
|
||||
onClick={() => setOpen(false)}
|
||||
name="God praksis"
|
||||
@@ -106,8 +90,8 @@ export const Hamburger = () => {
|
||||
/>
|
||||
</ul>
|
||||
</nav>
|
||||
</ReactModal>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -114,9 +114,8 @@ export const Header = ({
|
||||
<div className="z-[1050] ml-auto mr-4 flex justify-center lg:ml-0 lg:mr-0">
|
||||
<GlobalSearch />
|
||||
</div>
|
||||
<div className="mr-2 h-full lg:hidden">
|
||||
<Hamburger />
|
||||
</div>
|
||||
|
||||
<Hamburger />
|
||||
</div>
|
||||
</header>
|
||||
</>
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
/* stylelint-disable aksel/design-token-no-global-override */
|
||||
.modalOverlay {
|
||||
z-index: var(--a-z-index-modal);
|
||||
@apply bg-surface-backdrop fixed inset-0 grid place-items-center lg:hidden;
|
||||
}
|
||||
|
||||
.modalOverlaySearch {
|
||||
z-index: 3000;
|
||||
@apply bg-deepblue-900/20 fixed inset-0 grid place-items-center;
|
||||
}
|
||||
|
||||
.akselTertiaryButton {
|
||||
--a-text-action: var(--a-deepblue-800);
|
||||
--a-text-action-on-action-subtle: var(--a-deepblue-800);
|
||||
|
||||
@@ -50,13 +50,7 @@ export const SearchForm = () => {
|
||||
onSubmit={(e) => e.preventDefault()}
|
||||
className="bg-surface-default grid w-full gap-2"
|
||||
>
|
||||
<div className="flex flex-row-reverse items-center gap-1 p-1">
|
||||
<Button
|
||||
variant="tertiary-neutral"
|
||||
icon={<XMarkIcon title="Lukk søk" />}
|
||||
onClick={close}
|
||||
type="button"
|
||||
/>
|
||||
<div className="flex items-center gap-1 p-1">
|
||||
<Search
|
||||
label={
|
||||
<span className="flex items-center gap-2">
|
||||
@@ -83,6 +77,15 @@ export const SearchForm = () => {
|
||||
placeholder="Søk gjennom hele aksel"
|
||||
className="border-none pr-0 focus:shadow-none"
|
||||
/>
|
||||
<div className="grid h-full w-12 place-content-center">
|
||||
<Button
|
||||
variant="tertiary-neutral"
|
||||
icon={<XMarkIcon title="Lukk søk" />}
|
||||
onClick={close}
|
||||
type="button"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{chipsToShow.length !== 0 && (
|
||||
<Chips className="px-4 pb-4 md:px-10">
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { MagnifyingGlassIcon } from "@navikt/aksel-icons";
|
||||
import { Button } from "@navikt/ds-react";
|
||||
import cl from "clsx";
|
||||
import { useContext, useEffect } from "react";
|
||||
import ReactModal from "react-modal";
|
||||
import styles from "../header.module.css";
|
||||
import { Button, Modal } from "@navikt/ds-react";
|
||||
import { useContext } from "react";
|
||||
import { Error } from "./Error";
|
||||
import { SearchForm } from "./Form";
|
||||
import { Results } from "./Results";
|
||||
@@ -13,10 +10,6 @@ export const Search = () => {
|
||||
const { open, setOpen } = useContext(SearchContext);
|
||||
const { close } = useContext(SearchNavigationContext);
|
||||
|
||||
useEffect(() => {
|
||||
ReactModal.setAppElement("#__next");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
@@ -35,19 +28,17 @@ export const Search = () => {
|
||||
>
|
||||
Søk
|
||||
</Button>
|
||||
<ReactModal
|
||||
isOpen={open}
|
||||
onRequestClose={close}
|
||||
aria={{ modal: true }}
|
||||
contentLabel="Søk"
|
||||
className="bg-surface-default focus-visible:shadow-focus animate-searchModal shadow-xlarge absolute inset-0 mx-auto my-auto flex h-[90%] max-h-[52rem] w-full max-w-[90%] flex-col overflow-x-hidden rounded-lg focus:outline-none md:h-[80%] lg:max-w-4xl"
|
||||
overlayClassName={cl(styles.modalOverlaySearch, "backdrop-blur-[2px]")}
|
||||
htmlOpenClassName="overflow-hidden"
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={close}
|
||||
aria-label="Søk"
|
||||
className="h-[90%] max-h-[52rem] "
|
||||
width="medium"
|
||||
>
|
||||
<Error />
|
||||
<SearchForm />
|
||||
<Results />
|
||||
</ReactModal>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -9,8 +9,7 @@ import { SuggestionBlock } from "components/website-modules/suggestionblock";
|
||||
import Fuse from "fuse.js";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { categorizeIcons, getFillIcon } from "./utils";
|
||||
import { IconSidebar } from "./Sidebar";
|
||||
import { TitleLinks } from "./TitleLinks";
|
||||
@@ -70,12 +69,6 @@ export const IconPage = ({ name }: { name: string }) => {
|
||||
);
|
||||
}, [toggle, query, strokeIcons, fillIcons]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
hideModal && Modal.setAppElement("#__next");
|
||||
}, [hideModal]);
|
||||
|
||||
const subCategoryWordLength = Math.max(
|
||||
...categories.map((cat) =>
|
||||
Math.max(...cat.sub_categories.map((sub) => sub.sub_category.length))
|
||||
@@ -260,15 +253,8 @@ export const IconPage = ({ name }: { name: string }) => {
|
||||
{!hideModal && (
|
||||
<Modal
|
||||
open={!!name}
|
||||
onClose={() =>
|
||||
router.push(`/ikoner#${name}`, undefined, {
|
||||
shallow: true,
|
||||
})
|
||||
}
|
||||
closeButton={false}
|
||||
aria-modal
|
||||
aria-label={`${name} ikon`}
|
||||
className="bg-surface-default focus-visible:shadow-focus z-modal absolute block h-full overflow-y-auto rounded px-2 py-6 focus:outline-none sm:px-6"
|
||||
className="rounded px-2 py-6 sm:px-6"
|
||||
>
|
||||
{name && <IconSidebar name={name} focusRef={focusRef} />}
|
||||
</Modal>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const getImage = (key: string, variant: "OG" | "thumbnail") => {
|
||||
const bloggOptions = 11;
|
||||
const bloggOptions = 9;
|
||||
|
||||
const hash = Math.abs(
|
||||
key.split("").reduce(function (a, b) {
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
"prismjs": "^1.27.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-modal": "3.15.1",
|
||||
"react-responsive-masonry": "^2.1.7",
|
||||
"sanity": "3.15.1",
|
||||
"sanity-plugin-asset-source-unsplash": "^1.1.0",
|
||||
|
||||
@@ -1,44 +1,25 @@
|
||||
import {
|
||||
DatePicker,
|
||||
useDatepicker,
|
||||
Button,
|
||||
Modal,
|
||||
Heading,
|
||||
} from "@navikt/ds-react";
|
||||
import { DatePicker, useDatepicker, Button, Modal } from "@navikt/ds-react";
|
||||
import { withDsExample } from "components/website-modules/examples/withDsExample";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRef } from "react";
|
||||
import format from "date-fns/format";
|
||||
import nbLocale from "date-fns/locale/nb";
|
||||
|
||||
const Example = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const ref = useRef<HTMLDialogElement>(null);
|
||||
|
||||
const { datepickerProps, inputProps, selectedDay } = useDatepicker({
|
||||
fromDate: new Date("Aug 23 2019"),
|
||||
onDateChange: console.log,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
Modal.setAppElement("#__next");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>Åpne modal</Button>
|
||||
<Button onClick={() => ref.current?.showModal()}>Åpne modal</Button>
|
||||
|
||||
<Modal
|
||||
open={open}
|
||||
aria-label="Modal demo"
|
||||
onClose={() => setOpen((x) => !x)}
|
||||
shouldCloseOnEsc={!datepickerProps.open}
|
||||
aria-labelledby="modal-heading"
|
||||
>
|
||||
<Modal.Content className="min-w-96 max-w-full">
|
||||
<Heading spacing level="1" size="large" id="modal-heading">
|
||||
Heading
|
||||
</Heading>
|
||||
<div className="min-h-96">
|
||||
<DatePicker {...datepickerProps} strategy="fixed">
|
||||
<Modal ref={ref} header={{ heading: "Heading" }}>
|
||||
<Modal.Body>
|
||||
<div className="min-h-96 min-w-96 max-w-full">
|
||||
<DatePicker {...datepickerProps}>
|
||||
<DatePicker.Input {...inputProps} label="Velg dato" />
|
||||
</DatePicker>
|
||||
<div className="pt-4">
|
||||
@@ -46,7 +27,7 @@ const Example = () => {
|
||||
format(selectedDay, "dd.MM.yyyy", { locale: nbLocale })}
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
@@ -61,5 +42,4 @@ export const Demo = {
|
||||
|
||||
export const args = {
|
||||
index: 11,
|
||||
desc: "Ved bruk av datepicker i Modal er det viktig at 'Escape' ikke lukker selve modalen hvis datepicker er åpen. Bruk 'shouldCloseOnEsc' for å toggle dette når datepicker er åpen.",
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
import { BodyLong, Button, Heading, Modal } from "@navikt/ds-react";
|
||||
import { withDsExample } from "components/website-modules/examples/withDsExample";
|
||||
import { useState } from "react";
|
||||
|
||||
const Example = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>Åpne modal</Button>
|
||||
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="modal-heading"
|
||||
>
|
||||
<Modal.Header>
|
||||
<Heading level="1" size="large" id="modal-heading">
|
||||
Laborum proident id ullamco
|
||||
</Heading>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<BodyLong>
|
||||
Culpa aliquip ut cupidatat laborum minim quis ex in aliqua. Qui
|
||||
incididunt dolor do ad ut. Incididunt eiusmod nostrud deserunt duis
|
||||
laborum. Proident aute culpa qui nostrud velit adipisicing minim.
|
||||
Consequat aliqua aute dolor do sit Lorem nisi mollit velit. Aliqua
|
||||
exercitation non minim minim pariatur sunt laborum ipsum.
|
||||
Exercitation nostrud est laborum magna non non aliqua qui esse.
|
||||
</BodyLong>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default withDsExample(Example);
|
||||
|
||||
/* Storybook story */
|
||||
export const Demo = {
|
||||
render: Example,
|
||||
};
|
||||
|
||||
export const args = {
|
||||
index: 3,
|
||||
desc: "Headeren tilbys som en egen komponent for de tilfellene du trenger mer fleksiblitet enn header-propen gir deg. Begge varianter har mulighet for å skjule lukkeknappen.",
|
||||
};
|
||||
@@ -1,31 +1,17 @@
|
||||
import { BodyLong, Button, Heading, Modal } from "@navikt/ds-react";
|
||||
import { BodyLong, Button, Modal } from "@navikt/ds-react";
|
||||
import { withDsExample } from "components/website-modules/examples/withDsExample";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRef } from "react";
|
||||
|
||||
const Example = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
Modal.setAppElement("#__next");
|
||||
}, []);
|
||||
const ref = useRef<HTMLDialogElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>Åpne modal</Button>
|
||||
<Modal
|
||||
open={open}
|
||||
aria-label="Modal demo"
|
||||
onClose={() => setOpen((x) => !x)}
|
||||
aria-labelledby="modal-heading"
|
||||
>
|
||||
<Modal.Content>
|
||||
<Heading spacing level="1" size="large" id="modal-heading">
|
||||
Laborum proident id ullamco
|
||||
</Heading>
|
||||
<Heading spacing level="2" size="medium">
|
||||
Excepteur labore nostrud incididunt exercitation.
|
||||
</Heading>
|
||||
<BodyLong spacing>
|
||||
<div className="py-16">
|
||||
<Button onClick={() => ref.current?.showModal()}>Åpne modal</Button>
|
||||
|
||||
<Modal ref={ref} header={{ heading: "Overskrift" }}>
|
||||
<Modal.Body>
|
||||
<BodyLong>
|
||||
Culpa aliquip ut cupidatat laborum minim quis ex in aliqua. Qui
|
||||
incididunt dolor do ad ut. Incididunt eiusmod nostrud deserunt duis
|
||||
laborum. Proident aute culpa qui nostrud velit adipisicing minim.
|
||||
@@ -33,9 +19,28 @@ const Example = () => {
|
||||
exercitation non minim minim pariatur sunt laborum ipsum.
|
||||
Exercitation nostrud est laborum magna non non aliqua qui esse.
|
||||
</BodyLong>
|
||||
</Modal.Content>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button type="button" onClick={() => ref.current?.close()}>
|
||||
Primær
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => ref.current?.close()}
|
||||
>
|
||||
Sekundær
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="tertiary"
|
||||
onClick={() => ref.current?.close()}
|
||||
>
|
||||
Tertiær
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Button, Modal, TextField } from "@navikt/ds-react";
|
||||
import { withDsExample } from "components/website-modules/examples/withDsExample";
|
||||
import { useRef } from "react";
|
||||
|
||||
const Example = () => {
|
||||
const ref = useRef<HTMLDialogElement>(null);
|
||||
|
||||
return (
|
||||
<div className="py-12">
|
||||
<Button onClick={() => ref.current?.showModal()}>Åpne modal</Button>
|
||||
|
||||
<Modal ref={ref} header={{ heading: "Skjema" }} width={400}>
|
||||
<Modal.Body>
|
||||
<form method="dialog" id="skjema" onSubmit={(e) => alert("onSubmit")}>
|
||||
<TextField label="Har du noen tilbakemeldinger?" />
|
||||
</form>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button form="skjema">Send</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => ref.current?.close()}
|
||||
>
|
||||
Avbryt
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withDsExample(Example);
|
||||
|
||||
/* Storybook story */
|
||||
export const Demo = {
|
||||
render: Example,
|
||||
};
|
||||
|
||||
export const args = {
|
||||
index: 4,
|
||||
desc: 'Det er ikke støtte for å ha <form> som direkte barn av <Modal>. Legg <form> enten rundt hele modalen eller inni <Modal.Body>. Sistnevnte gjør det mulig å sette method="dialog", som gjør at modalen lukkes ved submit.',
|
||||
};
|
||||
@@ -1,40 +1,43 @@
|
||||
import { BodyLong, Button, Heading, Modal } from "@navikt/ds-react";
|
||||
import { BodyLong, Button, Modal } from "@navikt/ds-react";
|
||||
import { withDsExample } from "components/website-modules/examples/withDsExample";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
const Example = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
Modal.setAppElement("#__next");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>Åpne modal</Button>
|
||||
|
||||
<Modal
|
||||
open={open}
|
||||
aria-label="Modal demo"
|
||||
onClose={() => setOpen((x) => !x)}
|
||||
closeButton={false}
|
||||
aria-labelledby="modal-heading"
|
||||
onClose={() => setOpen(false)}
|
||||
header={{
|
||||
heading: "Er du sikker?",
|
||||
size: "small",
|
||||
closeButton: false,
|
||||
}}
|
||||
width="small"
|
||||
>
|
||||
<Modal.Content>
|
||||
<Heading spacing level="1" size="large" id="modal-heading">
|
||||
Laborum proident id ullamco
|
||||
</Heading>
|
||||
<Heading spacing level="2" size="medium">
|
||||
Excepteur labore nostrud incididunt exercitation.
|
||||
</Heading>
|
||||
<BodyLong spacing>
|
||||
<Modal.Body>
|
||||
<BodyLong>
|
||||
Culpa aliquip ut cupidatat laborum minim quis ex in aliqua. Qui
|
||||
incididunt dolor do ad ut. Incididunt eiusmod nostrud deserunt duis
|
||||
laborum. Proident aute culpa qui nostrud velit adipisicing minim.
|
||||
Consequat aliqua aute dolor do sit Lorem nisi mollit velit. Aliqua
|
||||
exercitation non minim minim pariatur sunt laborum ipsum.
|
||||
Exercitation nostrud est laborum magna non non aliqua qui esse.
|
||||
laborum.
|
||||
</BodyLong>
|
||||
</Modal.Content>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button type="button" variant="danger" onClick={() => setOpen(false)}>
|
||||
Ja, jeg er sikker
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
Avbryt
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
@@ -48,5 +51,6 @@ export const Demo = {
|
||||
};
|
||||
|
||||
export const args = {
|
||||
index: 1,
|
||||
index: 2,
|
||||
desc: "Lukkeknappen i headeren kan skjules, men da må du ha en annen knapp som lukker modalen.",
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import { FileIcon } from "@navikt/aksel-icons";
|
||||
import { BodyLong, Button, Modal } from "@navikt/ds-react";
|
||||
import { withDsExample } from "components/website-modules/examples/withDsExample";
|
||||
import { useRef } from "react";
|
||||
|
||||
const Example = () => {
|
||||
const ref = useRef<HTMLDialogElement>(null);
|
||||
|
||||
return (
|
||||
<div className="py-16">
|
||||
<Button onClick={() => ref.current?.showModal()}>Åpne modal</Button>
|
||||
|
||||
<Modal
|
||||
ref={ref}
|
||||
header={{
|
||||
label: "Modal demo",
|
||||
icon: <FileIcon aria-hidden />,
|
||||
heading: "Overskrift",
|
||||
}}
|
||||
>
|
||||
<Modal.Body>
|
||||
<BodyLong>
|
||||
Culpa aliquip ut cupidatat laborum minim quis ex in aliqua. Qui
|
||||
incididunt dolor do ad ut. Incididunt eiusmod nostrud deserunt duis
|
||||
laborum. Proident aute culpa qui nostrud velit adipisicing minim.
|
||||
Consequat aliqua aute dolor do sit Lorem nisi mollit velit. Aliqua
|
||||
exercitation non minim minim pariatur sunt laborum ipsum.
|
||||
Exercitation nostrud est laborum magna non non aliqua qui esse.
|
||||
</BodyLong>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button type="button" onClick={() => ref.current?.close()}>
|
||||
Primær
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => ref.current?.close()}
|
||||
>
|
||||
Sekundær
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="tertiary"
|
||||
onClick={() => ref.current?.close()}
|
||||
>
|
||||
Tertiær
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withDsExample(Example);
|
||||
|
||||
/* Storybook story */
|
||||
export const Demo = {
|
||||
render: Example,
|
||||
};
|
||||
|
||||
export const args = {
|
||||
index: 1,
|
||||
};
|
||||
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 14 KiB |
@@ -1,16 +0,0 @@
|
||||
<svg width="1200" height="675" viewBox="0 0 1200 675" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1090_180341)">
|
||||
<rect width="1200" height="675" fill="#8669C6"/>
|
||||
<path d="M935 548L268 -119L932.999 -118.001L1600 549L935 548Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M933.34 -117.66L266.68 549L933.34 1215.66L1600 549L933.34 -117.66Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M267 -118L-400 549L266 549L933 -118L267 -118Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M266.666 -117.666L-400 549L266.666 1215.67L933.332 549L266.666 -117.666Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M934 548.999L267 1216L933 1216L1600 548.999L934 548.999Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M267 1216L-400 549L265.999 548.999L933 1216L267 1216Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1090_180341">
|
||||
<rect width="1200" height="675" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,16 +0,0 @@
|
||||
<svg width="1200" height="675" viewBox="0 0 1200 675" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1090_180382)">
|
||||
<rect width="1200" height="675" fill="#EC4899"/>
|
||||
<path d="M781.57 336.458L420.056 -25.056L780.486 -24.5145L1142 337L781.57 336.458Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M780.67 -24.3296L419.34 337L780.67 698.33L1142 337L780.67 -24.3296Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M419.514 -24.514L58.0001 337L418.972 337L780.486 -24.514L419.514 -24.514Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M419.334 -24.333L58.001 337L419.334 698.333L780.667 337L419.334 -24.333Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M781.028 337L419.514 698.514L780.486 698.514L1142 337L781.028 337Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M419.514 698.514L58.0003 337L418.972 337L780.486 698.514L419.514 698.514Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1090_180382">
|
||||
<rect width="1200" height="675" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,12 +1,12 @@
|
||||
<svg width="1200" height="675" viewBox="0 0 1200 675" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1090_180364)">
|
||||
<rect width="1200" height="675" fill="#CCE2F0"/>
|
||||
<path d="M1395.31 -431.082L965.724 -1.49527L1395.31 428.092L1824.9 -1.4952L1395.31 -431.082Z" stroke="#99C4DD" stroke-width="16" stroke-linejoin="round"/>
|
||||
<path d="M542.92 -7.63999L113.331 421.949L542.276 421.949L971.865 -7.63995L542.92 -7.63999Z" stroke="#99C4DD" stroke-width="16" stroke-linejoin="round"/>
|
||||
<path d="M119.473 415.805L-310.114 845.392L119.473 1274.98L549.059 845.392L119.473 415.805Z" stroke="#99C4DD" stroke-width="16" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#clip0_1090_180352)">
|
||||
<rect width="1200" height="675" fill="#003453"/>
|
||||
<path d="M547.789 -495.224L118.202 -65.6368L547.789 363.95L977.376 -65.6369L547.789 -495.224Z" stroke="#99F6E4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M548.279 363.463L118.69 793.052L547.635 793.052L977.223 363.463L548.279 363.463Z" stroke="#99F6E4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M-310.409 1222.15L-739.998 792.564L-311.054 792.563L118.536 1222.15L-310.409 1222.15Z" stroke="#99F6E4" stroke-width="8" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1090_180364">
|
||||
<clipPath id="clip0_1090_180352">
|
||||
<rect width="1200" height="675" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 774 B After Width: | Height: | Size: 773 B |
@@ -1,12 +1,12 @@
|
||||
<svg width="1200" height="675" viewBox="0 0 1200 675" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1090_180352)">
|
||||
<rect width="1200" height="675" fill="#003453"/>
|
||||
<path d="M547.789 -495.224L118.202 -65.6368L547.789 363.95L977.376 -65.6369L547.789 -495.224Z" stroke="#99F6E4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M548.279 363.463L118.69 793.052L547.635 793.052L977.223 363.463L548.279 363.463Z" stroke="#99F6E4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M-310.409 1222.15L-739.998 792.564L-311.054 792.563L118.536 1222.15L-310.409 1222.15Z" stroke="#99F6E4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#clip0_1090_180360)">
|
||||
<rect width="1200" height="675" fill="#FDF193"/>
|
||||
<path d="M1485.82 -182.939L1056.23 -612.528L1485.18 -612.528L1914.77 -182.939L1485.82 -182.939Z" stroke="#9862DB" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M1053.25 -180.596L623.665 248.991L1053.25 678.577L1482.84 248.991L1053.25 -180.596Z" stroke="#9862DB" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M192.377 251.33L-237.211 680.919L191.733 680.919L621.322 251.33L192.377 251.33Z" stroke="#9862DB" stroke-width="8" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1090_180352">
|
||||
<clipPath id="clip0_1090_180360">
|
||||
<rect width="1200" height="675" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 773 B After Width: | Height: | Size: 772 B |
@@ -1,12 +1,11 @@
|
||||
<svg width="1200" height="675" viewBox="0 0 1200 675" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1090_180360)">
|
||||
<rect width="1200" height="675" fill="#FDF193"/>
|
||||
<path d="M1485.82 -182.939L1056.23 -612.528L1485.18 -612.528L1914.77 -182.939L1485.82 -182.939Z" stroke="#9862DB" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M1053.25 -180.596L623.665 248.991L1053.25 678.577L1482.84 248.991L1053.25 -180.596Z" stroke="#9862DB" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M192.377 251.33L-237.211 680.919L191.733 680.919L621.322 251.33L192.377 251.33Z" stroke="#9862DB" stroke-width="8" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#clip0_1090_180372)">
|
||||
<rect width="1200" height="675" fill="#FBCFE8"/>
|
||||
<path d="M2296.82 -94.5665L618.39 -94.5666L1456.35 743.392L3134.78 743.392L2296.82 -94.5665Z" stroke="#F472B6" stroke-width="16" stroke-linejoin="round"/>
|
||||
<path d="M-238.001 -94.5665L-238.001 -1773L599.957 -935.043L599.957 743.392L-238.001 -94.5665Z" stroke="#F472B6" stroke-width="16" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1090_180360">
|
||||
<clipPath id="clip0_1090_180372">
|
||||
<rect width="1200" height="675" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 623 B |
@@ -1,12 +1,15 @@
|
||||
<svg width="1200" height="675" viewBox="0 0 1200 675" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1090_180348)">
|
||||
<rect width="1200" height="675" fill="#99C4DD"/>
|
||||
<path d="M1125.92 -317.637L811.81 -3.52571L1125.92 310.585L1440.03 -3.52576L1125.92 -317.637Z" stroke="#E6F1F8" stroke-width="16" stroke-linejoin="round"/>
|
||||
<path d="M499.737 -5.09674L185.625 309.016L499.266 309.016L813.378 -5.09677L499.737 -5.09674Z" stroke="#E6F1F8" stroke-width="16" stroke-linejoin="round"/>
|
||||
<path d="M187.193 307.446L-126.918 621.557L187.193 935.668L501.304 621.557L187.193 307.446Z" stroke="#E6F1F8" stroke-width="16" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#clip0_1090_180341)">
|
||||
<rect width="1200" height="675" fill="#8669C6"/>
|
||||
<path d="M935 548L268 -119L932.999 -118.001L1600 549L935 548Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M933.34 -117.66L266.68 549L933.34 1215.66L1600 549L933.34 -117.66Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M267 -118L-400 549L266 549L933 -118L267 -118Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M266.666 -117.666L-400 549L266.666 1215.67L933.332 549L266.666 -117.666Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M934 548.999L267 1216L933 1216L1600 548.999L934 548.999Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M267 1216L-400 549L265.999 548.999L933 1216L267 1216Z" stroke="#D6C3EE" stroke-width="8" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1090_180348">
|
||||
<clipPath id="clip0_1090_180341">
|
||||
<rect width="1200" height="675" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 777 B After Width: | Height: | Size: 1.1 KiB |
@@ -1,11 +1,15 @@
|
||||
<svg width="1200" height="675" viewBox="0 0 1200 675" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1090_180372)">
|
||||
<rect width="1200" height="675" fill="#FBCFE8"/>
|
||||
<path d="M2296.82 -94.5665L618.39 -94.5666L1456.35 743.392L3134.78 743.392L2296.82 -94.5665Z" stroke="#F472B6" stroke-width="16" stroke-linejoin="round"/>
|
||||
<path d="M-238.001 -94.5665L-238.001 -1773L599.957 -935.043L599.957 743.392L-238.001 -94.5665Z" stroke="#F472B6" stroke-width="16" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#clip0_1090_180382)">
|
||||
<rect width="1200" height="675" fill="#EC4899"/>
|
||||
<path d="M781.57 336.458L420.056 -25.056L780.486 -24.5145L1142 337L781.57 336.458Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M780.67 -24.3296L419.34 337L780.67 698.33L1142 337L780.67 -24.3296Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M419.514 -24.514L58.0001 337L418.972 337L780.486 -24.514L419.514 -24.514Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M419.334 -24.333L58.001 337L419.334 698.333L780.667 337L419.334 -24.333Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M781.028 337L419.514 698.514L780.486 698.514L1142 337L781.028 337Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M419.514 698.514L58.0003 337L418.972 337L780.486 698.514L419.514 698.514Z" stroke="#F9A8D4" stroke-width="8" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1090_180372">
|
||||
<clipPath id="clip0_1090_180382">
|
||||
<rect width="1200" height="675" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 623 B After Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +1,5 @@
|
||||
html {
|
||||
/* Remove horizontal jump caused by scrollbar presens on long or short pages */
|
||||
overflow-y: scroll;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
code,
|
||||
@@ -58,11 +57,6 @@ a {
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop content behind modal-scrolling */
|
||||
.aksel.ReactModal__Body--open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.skiplink {
|
||||
@apply z-modal bg-border-focus text-text-on-inverted absolute left-0 -translate-y-full p-4 no-underline focus-within:translate-y-0 focus-within:shadow-[inset_0_0_0_2px_white] focus-within:outline-none;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ const ModalWrapper = () => {
|
||||
<>
|
||||
<Button onClick={() => setIsOpen(true)}>Open modal</Button>
|
||||
<Modal open={isOpen} onClose={() => setIsOpen(false)}>
|
||||
<Modal.Content>modal content</Modal.Content>
|
||||
<Modal.Body>modal content</Modal.Body>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "4.12.1",
|
||||
"@navikt/ds-react": "4.12.1",
|
||||
"@navikt/ds-css": "5.0.2",
|
||||
"@navikt/ds-react": "5.0.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
|
||||
@@ -3351,7 +3351,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@navikt/aksel-icons@^4.11.2, @navikt/aksel-icons@workspace:@navikt/aksel-icons":
|
||||
"@navikt/aksel-icons@^5.0.1, @navikt/aksel-icons@workspace:@navikt/aksel-icons":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@navikt/aksel-icons@workspace:@navikt/aksel-icons"
|
||||
dependencies:
|
||||
@@ -3378,8 +3378,8 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@navikt/aksel-stylelint@workspace:@navikt/aksel-stylelint"
|
||||
dependencies:
|
||||
"@navikt/ds-css": ^4.11.2
|
||||
"@navikt/ds-tokens": ^4.11.2
|
||||
"@navikt/ds-css": ^5.0.1
|
||||
"@navikt/ds-tokens": ^5.0.1
|
||||
"@types/jest": ^29.0.0
|
||||
concurrently: 7.2.1
|
||||
copyfiles: 2.4.1
|
||||
@@ -3396,7 +3396,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@navikt/aksel@workspace:@navikt/aksel"
|
||||
dependencies:
|
||||
"@navikt/ds-css": 4.11.2
|
||||
"@navikt/ds-css": 5.0.1
|
||||
"@types/inquirer": ^9.0.3
|
||||
"@types/jest": ^29.0.0
|
||||
axios: 1.3.6
|
||||
@@ -3420,11 +3420,11 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@navikt/ds-css@*, @navikt/ds-css@4.11.2, @navikt/ds-css@^4.11.2, @navikt/ds-css@workspace:@navikt/core/css":
|
||||
"@navikt/ds-css@*, @navikt/ds-css@5.0.1, @navikt/ds-css@^5.0.1, @navikt/ds-css@workspace:@navikt/core/css":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@navikt/ds-css@workspace:@navikt/core/css"
|
||||
dependencies:
|
||||
"@navikt/ds-tokens": ^4.11.2
|
||||
"@navikt/ds-tokens": ^5.0.1
|
||||
cssnano: 6.0.0
|
||||
fast-glob: 3.2.11
|
||||
lodash: 4.17.21
|
||||
@@ -3437,12 +3437,12 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@navikt/ds-react@*, @navikt/ds-react@4.11.2, @navikt/ds-react@^4.11.2, @navikt/ds-react@workspace:@navikt/core/react":
|
||||
"@navikt/ds-react@*, @navikt/ds-react@5.0.1, @navikt/ds-react@^5.0.1, @navikt/ds-react@workspace:@navikt/core/react":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@navikt/ds-react@workspace:@navikt/core/react"
|
||||
dependencies:
|
||||
"@floating-ui/react": 0.24.1
|
||||
"@navikt/aksel-icons": ^4.11.2
|
||||
"@navikt/aksel-icons": ^5.0.1
|
||||
"@radix-ui/react-tabs": 1.0.0
|
||||
"@radix-ui/react-toggle-group": 1.0.0
|
||||
"@testing-library/dom": 8.13.0
|
||||
@@ -3451,7 +3451,6 @@ __metadata:
|
||||
"@testing-library/user-event": ^14.2.0
|
||||
"@types/faker": 5.5.8
|
||||
"@types/jest": ^29.0.0
|
||||
"@types/react-modal": ^3.13.1
|
||||
clsx: ^1.2.1
|
||||
concurrently: 7.2.1
|
||||
copyfiles: ^2.4.1
|
||||
@@ -3462,7 +3461,6 @@ __metadata:
|
||||
jest-environment-jsdom: ^29.0.0
|
||||
react-day-picker: 8.3.4
|
||||
react-dom: ^18.0.0
|
||||
react-modal: 3.15.1
|
||||
react-router-dom: ^6.3.0
|
||||
rimraf: 3.0.2
|
||||
swr: ^1.1.2
|
||||
@@ -3478,11 +3476,11 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@navikt/ds-tailwind@^4.11.2, @navikt/ds-tailwind@workspace:@navikt/core/tailwind":
|
||||
"@navikt/ds-tailwind@^5.0.1, @navikt/ds-tailwind@workspace:@navikt/core/tailwind":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@navikt/ds-tailwind@workspace:@navikt/core/tailwind"
|
||||
dependencies:
|
||||
"@navikt/ds-tokens": ^4.11.2
|
||||
"@navikt/ds-tokens": ^5.0.1
|
||||
"@types/jest": ^29.0.0
|
||||
color: 4.2.3
|
||||
jest: ^29.0.0
|
||||
@@ -3493,7 +3491,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@navikt/ds-tokens@^4.11.2, @navikt/ds-tokens@workspace:@navikt/core/tokens":
|
||||
"@navikt/ds-tokens@^5.0.1, @navikt/ds-tokens@workspace:@navikt/core/tokens":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@navikt/ds-tokens@workspace:@navikt/core/tokens"
|
||||
dependencies:
|
||||
@@ -7281,15 +7279,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react-modal@npm:^3.13.1":
|
||||
version: 3.16.0
|
||||
resolution: "@types/react-modal@npm:3.16.0"
|
||||
dependencies:
|
||||
"@types/react": "*"
|
||||
checksum: 975c0238d81cea234a2318a7cfcb95a8e6beb7fc5341ca2311651535c6476881e8d2f2f2e1c679ca6e416c04505c748071a1fab0d3fb3e88b0a9e43060b012fe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react-redux@npm:^7.1.20":
|
||||
version: 7.1.25
|
||||
resolution: "@types/react-redux@npm:7.1.25"
|
||||
@@ -8044,11 +8033,11 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "aksel.nav.no@workspace:aksel.nav.no"
|
||||
dependencies:
|
||||
"@navikt/aksel-icons": ^4.11.2
|
||||
"@navikt/ds-css": ^4.11.2
|
||||
"@navikt/ds-react": ^4.11.2
|
||||
"@navikt/ds-tailwind": ^4.11.2
|
||||
"@navikt/ds-tokens": ^4.11.2
|
||||
"@navikt/aksel-icons": ^5.0.1
|
||||
"@navikt/ds-css": ^5.0.1
|
||||
"@navikt/ds-react": ^5.0.1
|
||||
"@navikt/ds-tailwind": ^5.0.1
|
||||
"@navikt/ds-tokens": ^5.0.1
|
||||
prettier-plugin-tailwindcss: ^0.2.3
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -12615,13 +12604,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"exenv@npm:^1.2.0":
|
||||
version: 1.2.2
|
||||
resolution: "exenv@npm:1.2.2"
|
||||
checksum: a894f3b60ab8419e0b6eec99c690a009c8276b4c90655ccaf7d28faba2de3a6b93b3d92210f9dc5efd36058d44f04098f6bbccef99859151104bfd49939904dc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"exif-component@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "exif-component@npm:1.0.1"
|
||||
@@ -20619,28 +20601,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-lifecycles-compat@npm:^3.0.0, react-lifecycles-compat@npm:^3.0.4":
|
||||
"react-lifecycles-compat@npm:^3.0.4":
|
||||
version: 3.0.4
|
||||
resolution: "react-lifecycles-compat@npm:3.0.4"
|
||||
checksum: a904b0fc0a8eeb15a148c9feb7bc17cec7ef96e71188280061fc340043fd6d8ee3ff233381f0e8f95c1cf926210b2c4a31f38182c8f35ac55057e453d6df204f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-modal@npm:3.15.1":
|
||||
version: 3.15.1
|
||||
resolution: "react-modal@npm:3.15.1"
|
||||
dependencies:
|
||||
exenv: ^1.2.0
|
||||
prop-types: ^15.7.2
|
||||
react-lifecycles-compat: ^3.0.0
|
||||
warning: ^4.0.3
|
||||
peerDependencies:
|
||||
react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
|
||||
react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
|
||||
checksum: ee99ca312c35bcec9ef0868babf970ce03c52801731e29be336bb6bdc867a1ecf00a73e1fb5bc3b1b1ef66ceb0c9b4a0199fadb85b1b9829f731409951b018f0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-photo-album@npm:2.2.2":
|
||||
version: 2.2.2
|
||||
resolution: "react-photo-album@npm:2.2.2"
|
||||
@@ -22059,8 +22026,8 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "shadow-dom@workspace:examples/shadow-dom"
|
||||
dependencies:
|
||||
"@navikt/ds-css": 4.11.2
|
||||
"@navikt/ds-react": 4.11.2
|
||||
"@navikt/ds-css": 5.0.1
|
||||
"@navikt/ds-react": 5.0.1
|
||||
"@types/react": ^18.0.0
|
||||
"@types/react-dom": ^18.0.0
|
||||
"@vitejs/plugin-react": ^3.1.0
|
||||
@@ -24916,15 +24883,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"warning@npm:^4.0.3":
|
||||
version: 4.0.3
|
||||
resolution: "warning@npm:4.0.3"
|
||||
dependencies:
|
||||
loose-envify: ^1.0.0
|
||||
checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"watchpack@npm:2.4.0, watchpack@npm:^2.2.0":
|
||||
version: 2.4.0
|
||||
resolution: "watchpack@npm:2.4.0"
|
||||
@@ -25052,7 +25010,6 @@ __metadata:
|
||||
prismjs: ^1.27.0
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
react-modal: 3.15.1
|
||||
react-responsive-masonry: ^2.1.7
|
||||
sanity: 3.15.1
|
||||
sanity-plugin-asset-source-unsplash: ^1.1.0
|
||||
|
||||