Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bb5b8f7b9 | ||
|
|
dcf5cfc06b | ||
|
|
ff97e7f3f8 | ||
|
|
9e1165de0a | ||
|
|
812573ac43 | ||
|
|
5f340eba49 | ||
|
|
703e7e3ab5 | ||
|
|
b42101f6f1 | ||
|
|
8b0c4d8f59 | ||
|
|
6ab84e581f | ||
|
|
42f3a298a2 | ||
|
|
e49922d3fe | ||
|
|
fc06e8f7b4 | ||
|
|
0273b42fb4 | ||
|
|
0873fe6520 | ||
|
|
fdcf502a64 | ||
|
|
aebb22bf1b | ||
|
|
4529e64c6e | ||
|
|
01395e65ba | ||
|
|
21dd0450e7 | ||
|
|
ffac16cb05 | ||
|
|
98177d0cd3 | ||
|
|
2c5be718e6 | ||
|
|
019d92dd7b | ||
|
|
5b72afbb93 | ||
|
|
56348cdddf | ||
|
|
973d79ee99 | ||
|
|
5bfb912531 | ||
|
|
27e3dfee07 | ||
|
|
20b2bf6f89 | ||
|
|
4b870986c8 | ||
|
|
8fcf079359 | ||
|
|
54f8b07956 | ||
|
|
9bc251e694 | ||
|
|
e2a9d027dc | ||
|
|
22dfd8e603 | ||
|
|
53d1947311 | ||
|
|
d4e7c47772 | ||
|
|
e191dffdf8 | ||
|
|
7e0ee60d47 | ||
|
|
4ecaeb9d04 | ||
|
|
8e5c554431 | ||
|
|
727be0a98b | ||
|
|
4c2e44ced1 | ||
|
|
2fd1dd443f | ||
|
|
a6c3391237 | ||
|
|
5ede88a188 | ||
|
|
2785593d20 | ||
|
|
a37c743547 | ||
|
|
6f0e18c3cf | ||
|
|
bdfaa54e81 | ||
|
|
595796af6c | ||
|
|
c85b34e86f | ||
|
|
fc46b5a97b | ||
|
|
c7fcf57fdf | ||
|
|
560d8bd1e8 | ||
|
|
7a53de186e | ||
|
|
4feb798b25 | ||
|
|
c713b469f0 | ||
|
|
676392a5c4 | ||
|
|
c049e08555 | ||
|
|
d8c898db8c | ||
|
|
8a1ef5e7e2 | ||
|
|
293594235a | ||
|
|
a54d97591e | ||
|
|
dc2e7fb766 | ||
|
|
53df42df22 | ||
|
|
5b9840524c | ||
|
|
0d6ddfd95e | ||
|
|
0394e819ba | ||
|
|
67fc9ad560 | ||
|
|
8fe5c88eba | ||
|
|
1fbcf78745 | ||
|
|
83dc3236e8 | ||
|
|
7503c7fa7f | ||
|
|
e668181690 | ||
|
|
353dff3da5 | ||
|
|
a11993a155 | ||
|
|
7b53b7670f | ||
|
|
0aaa0c4e10 | ||
|
|
ea5e1f768c | ||
|
|
e6b16e9b31 | ||
|
|
1029d42163 | ||
|
|
fba5140d8b | ||
|
|
fe72f32dbb | ||
|
|
6a0890b42b | ||
|
|
320f45a775 | ||
|
|
3feb2283a4 | ||
|
|
c3b416a3cb | ||
|
|
36fba1a33b |
No files matched your search
@@ -22,9 +22,6 @@ jobs:
|
||||
- name: add tokens to .env
|
||||
run: |
|
||||
echo "SANITY_WRITE_KEY=${{ secrets.SANITY_FEEDBACK_WRITE_KEY }}" >> aksel.nav.no/website/.env
|
||||
echo "ALGOLIA_ADMIN=${{ secrets.ALGOLIA_ADMIN }}" >> aksel.nav.no/website/.env
|
||||
echo "ALGOLIA_ID=${{ secrets.ALGOLIA_ID}}" >> aksel.nav.no/website/.env
|
||||
echo "TRELLO_TOKEN=${{ secrets.TRELLO_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SANITY_PREVIEW_TOKEN=${{ secrets.SANITY_PREVIEW_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SANITY_PRIVATE_NO_DRAFTS=${{ secrets.SANITY_PRIVATE_NO_DRAFTS }}" >> aksel.nav.no/website/.env
|
||||
|
||||
@@ -21,9 +21,6 @@ jobs:
|
||||
- name: add tokens to .env
|
||||
run: |
|
||||
echo "SANITY_WRITE_KEY=${{ secrets.SANITY_FEEDBACK_WRITE_KEY }}" >> aksel.nav.no/website/.env
|
||||
echo "ALGOLIA_ADMIN=${{ secrets.ALGOLIA_ADMIN }}" >> aksel.nav.no/website/.env
|
||||
echo "ALGOLIA_ID=${{ secrets.ALGOLIA_ID}}" >> aksel.nav.no/website/.env
|
||||
echo "TRELLO_TOKEN=${{ secrets.TRELLO_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SANITY_PREVIEW_TOKEN=${{ secrets.SANITY_PREVIEW_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SANITY_PRIVATE_NO_DRAFTS=${{ secrets.SANITY_PRIVATE_NO_DRAFTS }}" >> aksel.nav.no/website/.env
|
||||
|
||||
@@ -22,18 +22,17 @@ jobs:
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd aksel.nav.no/sanity && yarn
|
||||
run: cd aksel.nav.no/website && yarn
|
||||
|
||||
- name: Export dataset
|
||||
run: cd aksel.nav.no/sanity && SANITY_TOKEN=${{secrets.SANITY_DATASET_EDITOR_TOKEN}} yarn backup
|
||||
run: cd aksel.nav.no/website && SANITY_PREVIEW_TOKEN=${{secrets.SANITY_DATASET_EDITOR_TOKEN}} yarn backup
|
||||
- name: Upload GCP bucket
|
||||
uses: "google-github-actions/auth@v0"
|
||||
with:
|
||||
credentials_json: "${{ secrets.gcp_credentials_prod }}"
|
||||
- uses: "google-github-actions/upload-cloud-storage@v0"
|
||||
with:
|
||||
path: ./aksel.nav.no/sanity
|
||||
path: ./aksel.nav.no/website
|
||||
destination: "aksel-website-prod/backups"
|
||||
glob: "backup-*.tar.gz"
|
||||
@@ -21,9 +21,6 @@ jobs:
|
||||
- name: add tokens to .env
|
||||
run: |
|
||||
echo "SANITY_WRITE_KEY=${{ secrets.SANITY_FEEDBACK_WRITE_KEY }}" >> aksel.nav.no/website/.env
|
||||
echo "ALGOLIA_ADMIN=${{ secrets.ALGOLIA_ADMIN }}" >> aksel.nav.no/website/.env
|
||||
echo "ALGOLIA_ID=${{ secrets.ALGOLIA_ID}}" >> aksel.nav.no/website/.env
|
||||
echo "TRELLO_TOKEN=${{ secrets.TRELLO_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SANITY_PREVIEW_TOKEN=${{ secrets.SANITY_PREVIEW_TOKEN }}" >> aksel.nav.no/website/.env
|
||||
echo "SANITY_PRIVATE_NO_DRAFTS=${{ secrets.SANITY_PRIVATE_NO_DRAFTS }}" >> aksel.nav.no/website/.env
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
name: Indexing search
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# 01.00 & 12.00 every day
|
||||
- cron: "00 01,12 * * *"
|
||||
|
||||
jobs:
|
||||
searchcrawl:
|
||||
name: Search-indexing aksel.nav.no
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
APPLICATION_ID: ${{ secrets.ALGOLIA_ID}}
|
||||
API_KEY: ${{ secrets.ALGOLIA_ADMIN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Read crawler.json
|
||||
id: algolia_config
|
||||
run: echo "::set-output name=config::$(cat ./aksel.nav.no/crawler.json | jq -r tostring)"
|
||||
- name: Push indices to Algolia
|
||||
uses: signcl/docsearch-scraper-action@master
|
||||
env:
|
||||
APPLICATION_ID: ${{ secrets.ALGOLIA_ID}}
|
||||
API_KEY: ${{ secrets.ALGOLIA_ADMIN }}
|
||||
CONFIG: ${{ steps.algolia_config.outputs.config }}
|
||||
@@ -149,4 +149,5 @@ tmp.json
|
||||
playwright-report/
|
||||
/test-results/
|
||||
backup-*.tar.gz
|
||||
sitemap-urls.json
|
||||
sitemap-urls.json
|
||||
*.tar.gz
|
||||
@@ -1,5 +1,23 @@
|
||||
# @navikt/ds-codemod
|
||||
|
||||
## 2.1.1
|
||||
|
||||
## 2.1.0
|
||||
|
||||
## 2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
## 2.0.16
|
||||
|
||||
## 2.0.15
|
||||
|
||||
## 2.0.14
|
||||
|
||||
## 2.0.13
|
||||
|
||||
## 2.0.12
|
||||
|
||||
## 2.0.11
|
||||
|
||||
## 2.0.10
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-codemod",
|
||||
"version": "2.0.11",
|
||||
"version": "2.1.1",
|
||||
"description": "Aksel codemods for NAVs designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
"build-pict": "svgr --silent --index-template index-template.js --out-dir src/pictograms pictograms"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navikt/ds-icons": "^2.0.11",
|
||||
"@navikt/ds-react": "^2.0.11",
|
||||
"@navikt/ds-icons": "^2.1.1",
|
||||
"@navikt/ds-react": "^2.1.1",
|
||||
"clsx": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 2.1.1
|
||||
|
||||
## 2.1.0
|
||||
|
||||
## 2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
## 2.0.16
|
||||
|
||||
## 2.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1748](https://github.com/navikt/Designsystemet/pull/1748) [`22dfd8e60`](https://github.com/navikt/Designsystemet/commit/22dfd8e60315c421749394d35e41f412a00a7593) Thanks [@KenAJoh](https://github.com/KenAJoh)! - toptextPosition prop i Chat for horisontal plassering av navn og dato.
|
||||
|
||||
## 2.0.14
|
||||
|
||||
## 2.0.13
|
||||
|
||||
## 2.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1737](https://github.com/navikt/Designsystemet/pull/1737) [`8a1ef5e7e`](https://github.com/navikt/Designsystemet/commit/8a1ef5e7e2c066cb80c13e14329e1585e2c427f8) Thanks [@KenAJoh](https://github.com/KenAJoh)! - :bug: Fikset default visuell error-state for radio
|
||||
|
||||
## 2.0.11
|
||||
|
||||
## 2.0.10
|
||||
|
||||
@@ -77,6 +77,22 @@
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.navds-chat--top-text-left .navds-chat__top-text {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.navds-chat--top-text-right .navds-chat__top-text {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.navds-chat--left .navds-chat__top-text--right {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.navds-chat--right .navds-chat__top-text--left {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.navds-chat__name {
|
||||
font-weight: var(--a-font-weight-bold);
|
||||
}
|
||||
@@ -143,27 +143,21 @@
|
||||
.navds-checkbox--error
|
||||
> .navds-checkbox__input:not(:hover):not(:disabled):not(:checked):not(:indeterminate)
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio--error
|
||||
> .navds-radio__input:not(:hover):not(:disabled):not(:checked):not(:indeterminate)
|
||||
+ .navds-radio__label::before {
|
||||
.navds-radio--error > .navds-radio__input:not(:hover):not(:disabled):not(:checked) + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger));
|
||||
}
|
||||
|
||||
.navds-checkbox--error
|
||||
> .navds-checkbox__input:focus:not(:hover):not(:disabled):not(:checked):not(:indeterminate)
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio--error
|
||||
> .navds-radio__input:focus:not(:hover):not(:disabled):not(:checked):not(:indeterminate)
|
||||
+ .navds-radio__label::before {
|
||||
.navds-radio--error > .navds-radio__input:focus:not(:hover):not(:disabled):not(:checked) + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger)), var(--a-shadow-focus);
|
||||
}
|
||||
|
||||
.navds-checkbox--error
|
||||
> .navds-checkbox__input:hover:not(:disabled):not(:checked):not(:indeterminate):not(:focus)
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio--error
|
||||
> .navds-radio__input:hover:not(:disabled):not(:checked):not(:indeterminate):not(:focus)
|
||||
+ .navds-radio__label::before {
|
||||
.navds-radio--error > .navds-radio__input:hover:not(:disabled):not(:checked):not(:focus) + .navds-radio__label::before {
|
||||
background-color: var(--ac-radio-checkbox-error-hover-bg, var(--a-surface-danger-subtle));
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger));
|
||||
}
|
||||
@@ -171,9 +165,7 @@
|
||||
.navds-checkbox--error
|
||||
> .navds-checkbox__input:focus:hover:not(:disabled):not(:checked):not(:indeterminate)
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio--error
|
||||
> .navds-radio__input:focus:hover:not(:disabled):not(:checked):not(:indeterminate)
|
||||
+ .navds-radio__label::before {
|
||||
.navds-radio--error > .navds-radio__input:focus:hover:not(:disabled):not(:checked) + .navds-radio__label::before {
|
||||
background-color: var(--ac-radio-checkbox-error-hover-bg, var(--a-surface-danger-subtle));
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger)), var(--a-shadow-focus);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "2.0.11",
|
||||
"version": "2.1.1",
|
||||
"description": "CSS for NAV Designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
@@ -20,7 +20,7 @@
|
||||
"watch": "postcss --watch --use postcss-import -o dist/index.css index.css"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^2.0.11",
|
||||
"@navikt/ds-tokens": "^2.1.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
"postcss": "^8.4.0",
|
||||
"postcss-cli": "^9.0.0",
|
||||
|
||||
@@ -1,5 +1,80 @@
|
||||
# @navikt/ds-react
|
||||
|
||||
## 2.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1759](https://github.com/navikt/Designsystemet/pull/1759) [`dcf5cfc06`](https://github.com/navikt/Designsystemet/commit/dcf5cfc06b751341559ef30085bd974820531e57) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Fikset @types/react v18 feil introdusert i v2.0.6
|
||||
|
||||
- Updated dependencies [[`dcf5cfc06`](https://github.com/navikt/Designsystemet/commit/dcf5cfc06b751341559ef30085bd974820531e57)]:
|
||||
- @navikt/ds-icons@2.1.1
|
||||
|
||||
## 2.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [`0873fe652`](https://github.com/navikt/Designsystemet/commit/0873fe6520e817e462197c162b49c05826da4838) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Datepicker/Monthpicker støtter nå år med 2 siffer i input
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-icons@2.1.0
|
||||
|
||||
## 2.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`98177d0cd`](https://github.com/navikt/Designsystemet/commit/98177d0cd3802029dcf398ce93c1767d3b0860c5) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Bedre typer for ref i datepicker
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-icons@2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`27e3dfee0`](https://github.com/navikt/Designsystemet/commit/27e3dfee07e135893e9f62cd32f80ed83a3510de)]:
|
||||
- @navikt/ds-icons@2.0.17
|
||||
|
||||
## 2.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`54f8b0795`](https://github.com/navikt/Designsystemet/commit/54f8b07956bbfb7de51f6682a5fc64903bfd1bdf)]:
|
||||
- @navikt/ds-icons@2.0.16
|
||||
|
||||
## 2.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1748](https://github.com/navikt/Designsystemet/pull/1748) [`22dfd8e60`](https://github.com/navikt/Designsystemet/commit/22dfd8e60315c421749394d35e41f412a00a7593) Thanks [@KenAJoh](https://github.com/KenAJoh)! - toptextPosition prop i Chat for horisontal plassering av navn og dato.
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-icons@2.0.15
|
||||
|
||||
## 2.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`8e5c55443`](https://github.com/navikt/Designsystemet/commit/8e5c554431c98b1b658c5054c0b3cf08b8a65e52) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Bumpet @floating-ui/react
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-icons@2.0.14
|
||||
|
||||
## 2.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`a6c339123`](https://github.com/navikt/Designsystemet/commit/a6c3391237f13394063e4c004ac9efb1875fc944)]:
|
||||
- @navikt/ds-icons@2.0.13
|
||||
|
||||
## 2.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-icons@2.0.12
|
||||
|
||||
## 2.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "2.0.11",
|
||||
"version": "2.1.1",
|
||||
"description": "Aksel react-components for NAV designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -36,8 +36,8 @@
|
||||
"test": "TZ=UTC jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom-interactions": "0.9.2",
|
||||
"@navikt/ds-icons": "^2.0.11",
|
||||
"@floating-ui/react": "0.17.0",
|
||||
"@navikt/ds-icons": "^2.1.1",
|
||||
"@radix-ui/react-tabs": "1.0.0",
|
||||
"@radix-ui/react-toggle-group": "1.0.0",
|
||||
"clsx": "^1.2.1",
|
||||
|
||||
@@ -19,6 +19,10 @@ export interface BubbleProps extends HTMLAttributes<HTMLDivElement> {
|
||||
* Background color on bubble
|
||||
*/
|
||||
backgroundColor?: string;
|
||||
/**
|
||||
* Overrides hoizontal position of toptext
|
||||
*/
|
||||
toptextPosition?: "left" | "right";
|
||||
}
|
||||
|
||||
export type BubbleType = React.ForwardRefExoticComponent<
|
||||
@@ -26,7 +30,18 @@ export type BubbleType = React.ForwardRefExoticComponent<
|
||||
>;
|
||||
|
||||
const Bubble: BubbleType = forwardRef(
|
||||
({ children, className, name, timestamp, backgroundColor, ...rest }, ref) => {
|
||||
(
|
||||
{
|
||||
children,
|
||||
className,
|
||||
name,
|
||||
timestamp,
|
||||
backgroundColor,
|
||||
toptextPosition,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
@@ -35,7 +50,12 @@ const Bubble: BubbleType = forwardRef(
|
||||
{...rest}
|
||||
>
|
||||
{(timestamp || name) && (
|
||||
<div className="navds-chat__top-text">
|
||||
<div
|
||||
className={cl(
|
||||
`navds-chat__top-text`,
|
||||
toptextPosition && `navds-chat__top-text--${toptextPosition}`
|
||||
)}
|
||||
>
|
||||
{name && <Detail className="navds-chat__name">{name}</Detail>}
|
||||
{timestamp && (
|
||||
<Detail className="navds-chat__timestamp">{timestamp}</Detail>
|
||||
|
||||
@@ -33,6 +33,11 @@ export interface ChatProps extends HTMLAttributes<HTMLDivElement> {
|
||||
* @default "left"
|
||||
*/
|
||||
position?: "left" | "right";
|
||||
/**
|
||||
* Hoizontal position of toptext
|
||||
* @default Same as chat
|
||||
*/
|
||||
toptextPosition?: "left" | "right";
|
||||
}
|
||||
|
||||
interface ChatComponent
|
||||
@@ -53,6 +58,7 @@ export const Chat = forwardRef<HTMLDivElement, ChatProps>(
|
||||
position = "left",
|
||||
avatarBgColor,
|
||||
backgroundColor,
|
||||
toptextPosition,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
@@ -60,7 +66,13 @@ export const Chat = forwardRef<HTMLDivElement, ChatProps>(
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cl("navds-chat", className, `navds-chat--${position}`)}
|
||||
className={cl(
|
||||
"navds-chat",
|
||||
className,
|
||||
`navds-chat--${position} navds-chat--top-text-${
|
||||
toptextPosition ?? position
|
||||
}`
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
<BodyShort
|
||||
|
||||
@@ -116,6 +116,57 @@ export const Position = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ToptextPosition = () => {
|
||||
return (
|
||||
<div className="colgap">
|
||||
<Chat
|
||||
avatar="ON"
|
||||
name="Ola Normann"
|
||||
timestamp="01.01.21 14:00"
|
||||
backgroundColor="rgba(255, 249, 240, 1)"
|
||||
avatarBgColor="rgba(255, 236, 204, 1)"
|
||||
toptextPosition="right"
|
||||
>
|
||||
<Chat.Bubble>
|
||||
Adipisicing laborum est eu laborum est sit in commodo enim sint
|
||||
laboris labore nisi ut.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
<Chat
|
||||
avatar="KH"
|
||||
name="Kari Høyli"
|
||||
timestamp="01.01.21 14:32"
|
||||
position="right"
|
||||
backgroundColor="rgba(230, 240, 255, 1)"
|
||||
avatarBgColor="rgba(204, 225, 255, 1)"
|
||||
toptextPosition="left"
|
||||
>
|
||||
<Chat.Bubble>
|
||||
Reprehenderit pariatur officia exercitation laboris.
|
||||
</Chat.Bubble>
|
||||
<Chat.Bubble>
|
||||
Enim velit deserunt do quis. Eu fugiat magna esse dolore ad sunt sit
|
||||
est dolore incididunt. Occaecat cupidatat magna.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
<Chat
|
||||
avatar="ON"
|
||||
name="Ola Normann"
|
||||
timestamp="01.01.21 15:00"
|
||||
backgroundColor="rgba(255, 249, 240, 1)"
|
||||
avatarBgColor="rgba(255, 236, 204, 1)"
|
||||
>
|
||||
<Chat.Bubble toptextPosition="right">
|
||||
Exercitation irure Lorem cupidatat culpa anim cillum esse ullamco qui
|
||||
dolore laborum et Lorem. Labore sunt duis id Lorem voluptate commodo
|
||||
ea esse. Dolore esse aliqua proident ea ad commodo ut dolore voluptate
|
||||
labore sunt aute.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Avatar = () => {
|
||||
return (
|
||||
<Chat avatar={<Illustration />} timestamp="01.01.21 14:00">
|
||||
|
||||
@@ -49,6 +49,13 @@ export interface UseDatepickerOptions
|
||||
* validation-callback
|
||||
*/
|
||||
onValidate?: (val: DateValidationT) => void;
|
||||
/**
|
||||
* Allows input of with 'yy' year format.
|
||||
* @default true
|
||||
* @Note Decision between 20th and 21st century is based on before(todays year - 80) ? 21st : 20th.
|
||||
* In 2023 this equals to 1943 - 2042
|
||||
*/
|
||||
allowTwoDigitYear?: boolean;
|
||||
}
|
||||
|
||||
interface UseDatepickerValue {
|
||||
@@ -59,7 +66,10 @@ interface UseDatepickerValue {
|
||||
/**
|
||||
* Use: <DatePicker.Input {...inputProps}/>
|
||||
*/
|
||||
inputProps: Pick<DateInputProps, "onChange" | "onFocus" | "onBlur" | "value">;
|
||||
inputProps: Pick<
|
||||
DateInputProps,
|
||||
"onChange" | "onFocus" | "onBlur" | "value"
|
||||
> & { ref: React.RefObject<HTMLInputElement> };
|
||||
/**
|
||||
* Resets all states (callback)
|
||||
*/
|
||||
@@ -112,6 +122,7 @@ export const useDatepicker = (
|
||||
inputFormat,
|
||||
onValidate,
|
||||
defaultMonth,
|
||||
allowTwoDigitYear = true,
|
||||
} = opt;
|
||||
|
||||
const locale = getLocaleFromString(_locale);
|
||||
@@ -188,7 +199,13 @@ export const useDatepicker = (
|
||||
|
||||
const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
|
||||
!open && setOpen(true);
|
||||
let day = parseDate(e.target.value, today, locale, "date");
|
||||
let day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"date",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
if (isValidDate(day)) {
|
||||
setMonth(day);
|
||||
setInputValue(formatDateForInput(day, locale, "date", inputFormat));
|
||||
@@ -196,7 +213,13 @@ export const useDatepicker = (
|
||||
};
|
||||
|
||||
const handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
|
||||
let day = parseDate(e.target.value, today, locale, "date");
|
||||
let day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"date",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
isValidDate(day) &&
|
||||
setInputValue(formatDateForInput(day, locale, "date", inputFormat));
|
||||
};
|
||||
@@ -227,7 +250,13 @@ export const useDatepicker = (
|
||||
// the calendar’s month.
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
setInputValue(e.target.value);
|
||||
const day = parseDate(e.target.value, today, locale, "date");
|
||||
const day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"date",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
|
||||
const isBefore =
|
||||
fromDate && day && differenceInCalendarDays(fromDate, day) > 0;
|
||||
|
||||
@@ -35,6 +35,13 @@ export interface UseMonthPickerOptions
|
||||
* Default shown year
|
||||
*/
|
||||
defaultYear?: Date;
|
||||
/**
|
||||
* Allows input of with 'yy' year format.
|
||||
* @default true
|
||||
* @Note Decision between 20th and 21st century is based on before(todays year - 80) ? 21st : 20th.
|
||||
* In 2023 this equals to 1943 - 2042
|
||||
*/
|
||||
allowTwoDigitYear?: boolean;
|
||||
}
|
||||
|
||||
interface UseMonthPickerValue {
|
||||
@@ -45,7 +52,9 @@ interface UseMonthPickerValue {
|
||||
/**
|
||||
* Use: <MonthPicker.Input {...inputProps} />
|
||||
*/
|
||||
inputProps: Pick<DateInputProps, "onChange" | "onFocus" | "value">;
|
||||
inputProps: Pick<DateInputProps, "onChange" | "onFocus" | "value"> & {
|
||||
ref: React.RefObject<HTMLInputElement>;
|
||||
};
|
||||
/**
|
||||
* Currently selected Date
|
||||
* Up to user to validate value and extract month
|
||||
@@ -94,6 +103,7 @@ export const useMonthpicker = (
|
||||
inputFormat,
|
||||
onValidate,
|
||||
defaultYear,
|
||||
allowTwoDigitYear = true,
|
||||
} = opt;
|
||||
|
||||
const [defaultSelected, setDefaultSelected] = useState(_defaultSelected);
|
||||
@@ -101,7 +111,7 @@ export const useMonthpicker = (
|
||||
const today = new Date();
|
||||
const locale = getLocaleFromString(_locale);
|
||||
|
||||
const inputRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const monthpickerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Initialize states
|
||||
@@ -171,7 +181,13 @@ export const useMonthpicker = (
|
||||
|
||||
const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
|
||||
!open && setOpen(true);
|
||||
let day = parseDate(e.target.value, today, locale, "month");
|
||||
let day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"month",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
if (isValidDate(day)) {
|
||||
setYear(day);
|
||||
setInputValue(formatDateForInput(day, locale, "month", inputFormat));
|
||||
@@ -179,7 +195,13 @@ export const useMonthpicker = (
|
||||
};
|
||||
|
||||
const handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
|
||||
let day = parseDate(e.target.value, today, locale, "month");
|
||||
let day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"month",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
isValidDate(day) &&
|
||||
setInputValue(formatDateForInput(day, locale, "month", inputFormat));
|
||||
};
|
||||
@@ -209,7 +231,13 @@ export const useMonthpicker = (
|
||||
// the calendar’s month.
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
setInputValue(e.target.value);
|
||||
const month = parseDate(e.target.value, today, locale, "month");
|
||||
const month = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"month",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
|
||||
const isBefore =
|
||||
fromDate &&
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import differenceInCalendarDays from "date-fns/differenceInCalendarDays";
|
||||
import isWeekend from "date-fns/isWeekend";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { DateRange, isMatch } from "react-day-picker";
|
||||
import { DateInputProps } from "../DateInput";
|
||||
import { DatePickerProps } from "../datepicker/DatePicker";
|
||||
@@ -71,14 +71,14 @@ interface UseRangeDatepickerValue {
|
||||
fromInputProps: Pick<
|
||||
DateInputProps,
|
||||
"onChange" | "onFocus" | "onBlur" | "value"
|
||||
>;
|
||||
> & { ref: React.RefObject<HTMLInputElement> };
|
||||
/**
|
||||
* Use: <DatePicker.Input label="to" {...toInputProps}/>
|
||||
*/
|
||||
toInputProps: Pick<
|
||||
DateInputProps,
|
||||
"onChange" | "onFocus" | "onBlur" | "value"
|
||||
>;
|
||||
> & { ref: React.RefObject<HTMLInputElement> };
|
||||
/**
|
||||
* Resets all states (callback)
|
||||
*/
|
||||
@@ -202,12 +202,13 @@ export const useRangeDatepicker = (
|
||||
inputFormat,
|
||||
onValidate,
|
||||
defaultMonth,
|
||||
allowTwoDigitYear = true,
|
||||
} = opt;
|
||||
|
||||
const locale = getLocaleFromString(_locale);
|
||||
|
||||
const inputRefTo = useRef<HTMLDivElement>(null);
|
||||
const inputRefFrom = useRef<HTMLDivElement>(null);
|
||||
const inputRefTo = useRef<HTMLInputElement>(null);
|
||||
const inputRefFrom = useRef<HTMLInputElement>(null);
|
||||
const datePickerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const [defaultSelected, setDefaultSelected] = useState(_defaultSelected);
|
||||
@@ -322,7 +323,13 @@ export const useRangeDatepicker = (
|
||||
|
||||
const handleFocus = (e, src: RangeT) => {
|
||||
!open && setOpen(true);
|
||||
let day = parseDate(e.target.value, today, locale, "date");
|
||||
let day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"date",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
if (isValidDate(day)) {
|
||||
setMonth(day);
|
||||
src === RANGE.FROM
|
||||
@@ -334,7 +341,13 @@ export const useRangeDatepicker = (
|
||||
};
|
||||
|
||||
const handleBlur = (e, src: RangeT) => {
|
||||
let day = parseDate(e.target.value, today, locale, "date");
|
||||
let day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"date",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
if (!isValidDate(day)) {
|
||||
return;
|
||||
}
|
||||
@@ -426,7 +439,13 @@ export const useRangeDatepicker = (
|
||||
}
|
||||
|
||||
if (toInputValue && !selectedRange?.to) {
|
||||
const toDay = parseDate(toInputValue, today, locale, "date");
|
||||
const toDay = parseDate(
|
||||
toInputValue,
|
||||
today,
|
||||
locale,
|
||||
"date",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
if (validateDay(toDay)) {
|
||||
updateRange({ from: day, to: toDay });
|
||||
setMonth(day);
|
||||
@@ -489,7 +508,13 @@ export const useRangeDatepicker = (
|
||||
|
||||
/* live-update datepicker based on changes in inputfields */
|
||||
const handleChange = (e, src: RangeT) => {
|
||||
const day = parseDate(e.target.value, today, locale, "date");
|
||||
const day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
locale,
|
||||
"date",
|
||||
allowTwoDigitYear
|
||||
);
|
||||
const isBefore = fromDate && differenceInCalendarDays(fromDate, day) > 0;
|
||||
const isAfter = toDate && differenceInCalendarDays(day, toDate) > 0;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ describe("Returns if year is at start or end of range", () => {
|
||||
expect(hasNextYear(new Date(), years, 1)).toBeTruthy();
|
||||
});
|
||||
test("Should have previous year (true)", () => {
|
||||
expect(hasNextYear(new Date("Aug 3 2023"), years, -1)).toBeTruthy();
|
||||
expect(hasNextYear(new Date("Aug 3 2024"), years, -1)).toBeTruthy();
|
||||
});
|
||||
test("Should not have next year (false)", () => {
|
||||
expect(hasNextYear(new Date("Aug 3 2029"), years, 1)).toBeFalsy();
|
||||
|
||||
@@ -2,7 +2,9 @@ import { parseDate } from "../parse-date";
|
||||
import nb from "date-fns/locale/nb";
|
||||
import { formatDateForInput } from "../format-date";
|
||||
|
||||
const parse = (inp: string) => parseDate(inp, new Date(), nb, "date");
|
||||
const parse = (inp: string) => parseDate(inp, new Date(), nb, "date", false);
|
||||
const parseTwoDigit = (inp: string) =>
|
||||
parseDate(inp, new Date(), nb, "date", true);
|
||||
|
||||
describe("Format date to correct output", () => {
|
||||
test("formatDateForInput", () => {
|
||||
@@ -19,4 +21,12 @@ describe("Format date to correct output", () => {
|
||||
"15.05.2022"
|
||||
);
|
||||
});
|
||||
test("formatDateForInput with twoDigitYears", () => {
|
||||
expect(formatDateForInput(parseTwoDigit("15/05/22"), nb, "date")).toEqual(
|
||||
"15.05.2022"
|
||||
);
|
||||
expect(formatDateForInput(parseTwoDigit("1/5/95"), nb, "date")).toEqual(
|
||||
"01.05.1995"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -4,11 +4,14 @@ import nb from "date-fns/locale/nb";
|
||||
import getMonth from "date-fns/getMonth";
|
||||
|
||||
const check = (inp: string) =>
|
||||
expect(isValidDate(parseDate(inp, new Date(), nb, "date")));
|
||||
expect(isValidDate(parseDate(inp, new Date(), nb, "date", false)));
|
||||
|
||||
const parse = (inp: string) => parseDate(inp, new Date(), nb, "date");
|
||||
const checkTwoDigit = (inp: string) =>
|
||||
expect(isValidDate(parseDate(inp, new Date(), nb, "date", true)));
|
||||
|
||||
describe("Parse date-inputs", () => {
|
||||
const parse = (inp: string) => parseDate(inp, new Date(), nb, "date", false);
|
||||
|
||||
describe("Parse date-inputs with 4-digit years", () => {
|
||||
test("No spaces", () => {
|
||||
check("11052022").toBeTruthy();
|
||||
check("15052022").toBeTruthy();
|
||||
@@ -34,6 +37,44 @@ describe("Parse date-inputs", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Parse date-inputs with 2-digit years", () => {
|
||||
test("No spaces", () => {
|
||||
checkTwoDigit("110522").toBeTruthy();
|
||||
checkTwoDigit("150522").toBeTruthy();
|
||||
});
|
||||
|
||||
test(". divider", () => {
|
||||
checkTwoDigit("1.5.22").toBeTruthy();
|
||||
checkTwoDigit("11.05.22").toBeTruthy();
|
||||
});
|
||||
|
||||
test("/ divider", () => {
|
||||
checkTwoDigit("1/5/22").toBeTruthy();
|
||||
checkTwoDigit("10/5/22").toBeTruthy();
|
||||
checkTwoDigit("1/05/22").toBeTruthy();
|
||||
checkTwoDigit("10/05/22").toBeTruthy();
|
||||
});
|
||||
|
||||
test("- divider", () => {
|
||||
checkTwoDigit("1-5-22").toBeTruthy();
|
||||
checkTwoDigit("10-5-22").toBeTruthy();
|
||||
checkTwoDigit("1-05-22").toBeTruthy();
|
||||
checkTwoDigit("10-05-22").toBeTruthy();
|
||||
});
|
||||
|
||||
test("Dissallow 1 and 3 digit years", () => {
|
||||
checkTwoDigit("11052").toBeFalsy();
|
||||
checkTwoDigit("1105222").toBeFalsy();
|
||||
checkTwoDigit("1105999").toBeFalsy();
|
||||
});
|
||||
|
||||
test("Dissallow date before year 1000", () => {
|
||||
checkTwoDigit("11050999").toBeFalsy();
|
||||
checkTwoDigit("11050010").toBeFalsy();
|
||||
checkTwoDigit("11051000").toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Parse date-inputs to correct month", () => {
|
||||
test("No spaces", () => {
|
||||
expect(getMonth(parse("11052022"))).toEqual(4);
|
||||
|
||||
@@ -10,7 +10,7 @@ export const dateIsInCurrentMonth = (
|
||||
|
||||
/** @private */
|
||||
export function isValidDate(day: Date): boolean {
|
||||
return !isNaN(day.getTime());
|
||||
return day && !isNaN(day?.getTime()) && day.getFullYear() > 999;
|
||||
}
|
||||
|
||||
export const hasNextYear = (year: Date, years: Date[], val: any): boolean => {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import parse from "date-fns/parse";
|
||||
import isBefore from "date-fns/isBefore";
|
||||
import sub from "date-fns/sub";
|
||||
import { isValidDate } from ".";
|
||||
|
||||
export const INPUT_DATE_STRING_FORMAT_DATE = "dd.MM.yyyy";
|
||||
@@ -22,7 +24,60 @@ const ALLOWED_INPUT_FORMATS_MONTH = [
|
||||
...ALLOWED_INPUT_FORMATS_DATE,
|
||||
];
|
||||
|
||||
const isTwoDigitYear = (dateString, today, locale, formats) => {
|
||||
export const parseDate = (
|
||||
date: string,
|
||||
today: Date,
|
||||
locale: Locale,
|
||||
type: "date" | "month",
|
||||
allowTwoDigitYear: boolean
|
||||
): Date => {
|
||||
let parsed;
|
||||
const ALLOWED_FORMATS =
|
||||
type === "date" ? ALLOWED_INPUT_FORMATS_DATE : ALLOWED_INPUT_FORMATS_MONTH;
|
||||
|
||||
if (allowTwoDigitYear) {
|
||||
for (const format of ALLOWED_FORMATS) {
|
||||
parsed = parse(date, format, today, { locale });
|
||||
if (
|
||||
isValidDate(parsed) &&
|
||||
!isTwoDigitYear(date, today, locale, ALLOWED_FORMATS)
|
||||
) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
for (const format of [
|
||||
...ALLOWED_FORMATS.map((x) => x.replace("yyyy", "yy")),
|
||||
]) {
|
||||
parsed = parse(date, format, today, { locale });
|
||||
if (
|
||||
isValidDate(parsed) &&
|
||||
isTwoDigitYear(date, today, locale, ALLOWED_FORMATS)
|
||||
) {
|
||||
const convertedDate = assignCenturyToDate(date, format, today, locale);
|
||||
|
||||
if (isValidDate(new Date(convertedDate))) {
|
||||
return new Date(convertedDate);
|
||||
} else {
|
||||
return new Date("Invalid date");
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Date("Invalid date");
|
||||
} else {
|
||||
for (const format of ALLOWED_FORMATS) {
|
||||
parsed = parse(date, format, today, { locale });
|
||||
if (
|
||||
isValidDate(parsed) &&
|
||||
!isTwoDigitYear(date, today, locale, ALLOWED_FORMATS)
|
||||
) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return new Date("Invalid date");
|
||||
}
|
||||
};
|
||||
|
||||
function isTwoDigitYear(dateString, today, locale, formats) {
|
||||
let parsed;
|
||||
const newFormat = formats.map((x) => x.replace("yyyy", "yy"));
|
||||
for (const format of newFormat) {
|
||||
@@ -32,25 +87,49 @@ const isTwoDigitYear = (dateString, today, locale, formats) => {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export const parseDate = (
|
||||
date: string,
|
||||
function assignCenturyToDate(
|
||||
dateStr: string,
|
||||
format: string,
|
||||
today: Date,
|
||||
locale: Locale,
|
||||
type: "date" | "month"
|
||||
): Date => {
|
||||
let parsed;
|
||||
const ALLOWED_FORMATS =
|
||||
type === "date" ? ALLOWED_INPUT_FORMATS_DATE : ALLOWED_INPUT_FORMATS_MONTH;
|
||||
for (const format of ALLOWED_FORMATS) {
|
||||
parsed = parse(date, format, today, { locale });
|
||||
if (
|
||||
isValidDate(parsed) &&
|
||||
!isTwoDigitYear(date, today, locale, ALLOWED_FORMATS)
|
||||
) {
|
||||
return parsed;
|
||||
}
|
||||
locale: Locale
|
||||
) {
|
||||
const date20Century = parse(
|
||||
appendCenturyToTwoYearDigitDateString(dateStr, "19"),
|
||||
format.replace("yy", "yyyy"),
|
||||
today,
|
||||
{ locale }
|
||||
);
|
||||
|
||||
const date21Century = parse(
|
||||
appendCenturyToTwoYearDigitDateString(dateStr, "20"),
|
||||
format.replace("yy", "yyyy"),
|
||||
today,
|
||||
{ locale }
|
||||
);
|
||||
|
||||
if (isValidDate(date20Century) && isValidDate(date21Century)) {
|
||||
return isBefore(
|
||||
date20Century,
|
||||
sub(new Date(), {
|
||||
years: 80,
|
||||
})
|
||||
)
|
||||
? date21Century
|
||||
: date20Century;
|
||||
}
|
||||
|
||||
return new Date("Invalid date");
|
||||
};
|
||||
}
|
||||
|
||||
function appendCenturyToTwoYearDigitDateString(
|
||||
dateString: string,
|
||||
century: "19" | "20"
|
||||
) {
|
||||
const twoDigitYear = dateString.slice(-2);
|
||||
return `${dateString.slice(
|
||||
0,
|
||||
dateString.length - 2
|
||||
)}${century}${twoDigitYear}`;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, InputHTMLAttributes } from "react";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, InputHTMLAttributes } from "react";
|
||||
import { BodyShort, Detail, omit } from "../..";
|
||||
import { FormFieldProps } from "../useFormField";
|
||||
import { useRadio } from "./useRadio";
|
||||
|
||||
@@ -23,7 +23,7 @@ export const Default = (props) => {
|
||||
value={props.controlled ? state : undefined}
|
||||
onChange={props.controlled ? setState : undefined}
|
||||
hideLegend={props.hideLegend}
|
||||
error={props.errorGroup ? "Errormelding" : undefined}
|
||||
error={props.error ? "Errormelding" : undefined}
|
||||
size={props?.size}
|
||||
>
|
||||
<Radio value="radio1">{props.children || "Apple"}</Radio>
|
||||
@@ -41,6 +41,7 @@ export const Default = (props) => {
|
||||
|
||||
Default.args = {
|
||||
controlled: false,
|
||||
error: false,
|
||||
size: "medium",
|
||||
legend: "Legend-tekst",
|
||||
radioDescription: false,
|
||||
|
||||
@@ -6,11 +6,15 @@ interface SVGRProps {
|
||||
titleId?: string;
|
||||
}
|
||||
|
||||
export const DefaultIllustration = ({
|
||||
export type DefaultIllustrationType = React.FunctionComponent<
|
||||
SVGProps<SVGSVGElement> & SVGRProps
|
||||
>;
|
||||
|
||||
export const DefaultIllustration: DefaultIllustrationType = ({
|
||||
title,
|
||||
titleId: _titleId,
|
||||
...props
|
||||
}: SVGProps<SVGSVGElement> & SVGRProps) => {
|
||||
}) => {
|
||||
let titleId: string | undefined = useId();
|
||||
titleId = title ? (_titleId ? _titleId : "title-" + titleId) : undefined;
|
||||
|
||||
|
||||
@@ -33,7 +33,12 @@ export interface LoaderProps extends SVGProps<SVGSVGElement> {
|
||||
variant?: "neutral" | "interaction" | "inverted";
|
||||
}
|
||||
|
||||
export const Loader = forwardRef<SVGSVGElement, LoaderProps>(
|
||||
/* Workaround for @types/react v17/v18 feil */
|
||||
export type LoaderType = React.ForwardRefExoticComponent<
|
||||
LoaderProps & React.RefAttributes<SVGSVGElement>
|
||||
>;
|
||||
|
||||
export const Loader: LoaderType = forwardRef<SVGSVGElement, LoaderProps>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
useDismiss,
|
||||
useFloating,
|
||||
useInteractions,
|
||||
} from "@floating-ui/react-dom-interactions";
|
||||
} from "@floating-ui/react";
|
||||
import cl from "clsx";
|
||||
import React, {
|
||||
forwardRef,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
useFocus,
|
||||
useHover,
|
||||
useInteractions,
|
||||
} from "@floating-ui/react-dom-interactions";
|
||||
} from "@floating-ui/react";
|
||||
import cl from "clsx";
|
||||
import React, {
|
||||
cloneElement,
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @navikt/ds-tailwind
|
||||
|
||||
## 2.1.1
|
||||
|
||||
## 2.1.0
|
||||
|
||||
## 2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
## 2.0.16
|
||||
|
||||
## 2.0.15
|
||||
|
||||
## 2.0.14
|
||||
|
||||
## 2.0.13
|
||||
|
||||
## 2.0.12
|
||||
|
||||
## 2.0.11
|
||||
|
||||
## 2.0.10
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tailwind",
|
||||
"version": "2.0.11",
|
||||
"version": "2.1.1",
|
||||
"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": "^2.0.11",
|
||||
"@navikt/ds-tokens": "^2.1.1",
|
||||
"@types/jest": "^29.0.0",
|
||||
"color": "4.2.3",
|
||||
"jest": "^29.0.0",
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# @navikt/ds-tokens
|
||||
|
||||
## 2.1.1
|
||||
|
||||
## 2.1.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1665](https://github.com/navikt/Designsystemet/pull/1665) [`6ab84e581`](https://github.com/navikt/Designsystemet/commit/6ab84e581f8d3e850bcec340608354335506a051) Thanks [@KenAJoh](https://github.com/KenAJoh)! - :sparkles: Ny Timeline-komponent for interne-flater
|
||||
:bug: Fikset feil danger-hover token
|
||||
|
||||
## 2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
## 2.0.16
|
||||
|
||||
## 2.0.15
|
||||
|
||||
## 2.0.14
|
||||
|
||||
## 2.0.13
|
||||
|
||||
## 2.0.12
|
||||
|
||||
## 2.0.11
|
||||
|
||||
## 2.0.10
|
||||
|
||||
@@ -240,7 +240,7 @@
|
||||
{ "name": "--a-surface-danger-subtle", "value": "rgba(255, 230, 230, 1)" },
|
||||
{
|
||||
"name": "--a-surface-danger-subtle-hover",
|
||||
"value": "rgba(255, 230, 230, 1)"
|
||||
"value": "rgba(255, 184, 184, 1)"
|
||||
},
|
||||
{ "name": "--a-surface-danger", "value": "rgba(195, 0, 0, 1)" },
|
||||
{ "name": "--a-surface-danger-hover", "value": "rgba(173, 0, 0, 1)" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tokens",
|
||||
"version": "2.0.11",
|
||||
"version": "2.1.1",
|
||||
"description": "Design-tokens for NAV designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
|
||||
"danger": {
|
||||
"subtle": {
|
||||
"hover": { "value": "{a.red.50.value}" },
|
||||
"hover": { "value": "{a.red.100.value}" },
|
||||
"@": { "value": "{a.red.50.value}" }
|
||||
},
|
||||
"hover": {
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# @navikt/ds-icons
|
||||
|
||||
## 2.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1759](https://github.com/navikt/Designsystemet/pull/1759) [`dcf5cfc06`](https://github.com/navikt/Designsystemet/commit/dcf5cfc06b751341559ef30085bd974820531e57) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Fikset @types/react v18 feil introdusert i v2.0.6
|
||||
|
||||
## 2.1.0
|
||||
|
||||
## 2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`27e3dfee0`](https://github.com/navikt/Designsystemet/commit/27e3dfee07e135893e9f62cd32f80ed83a3510de) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Translate og Paragraph ikon
|
||||
|
||||
## 2.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`54f8b0795`](https://github.com/navikt/Designsystemet/commit/54f8b07956bbfb7de51f6682a5fc64903bfd1bdf) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Oppdatert ikoner med feile farger
|
||||
|
||||
## 2.0.15
|
||||
|
||||
## 2.0.14
|
||||
|
||||
## 2.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`a6c339123`](https://github.com/navikt/Designsystemet/commit/a6c3391237f13394063e4c004ac9efb1875fc944) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Ikonjusteringer sirkleikoner
|
||||
|
||||
## 2.0.12
|
||||
|
||||
## 2.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Fikser https://github.com/navikt/Designsystemet/issues/1758
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const files = fs.readdirSync(`./esm`).filter((x) => x.endsWith(".d.ts"));
|
||||
|
||||
files.forEach((file) => {
|
||||
let data = fs.readFileSync(`./esm/${file}`).toString().split("\n");
|
||||
|
||||
data = data.map((x) => {
|
||||
return x.includes("React.ForwardRefExoticComponent")
|
||||
? x.split(":")[0] +
|
||||
`: React.ForwardRefExoticComponent<React.SVGProps<SVGSVGElement> & SVGRProps & React.RefAttributes<SVGSVGElement>>;`
|
||||
: x;
|
||||
});
|
||||
|
||||
fs.writeFileSync(`./esm/${file}`, data.join("\n"));
|
||||
});
|
||||
@@ -867,13 +867,13 @@
|
||||
"name": "Eu",
|
||||
"description": "",
|
||||
"created_at": "2022-02-08T11:45:47.104Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "EuFilled",
|
||||
"description": "",
|
||||
"created_at": "2022-02-08T11:45:47.109Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "Expand",
|
||||
@@ -1479,13 +1479,13 @@
|
||||
"name": "Law",
|
||||
"description": "",
|
||||
"created_at": "2020-07-01T10:37:42.819Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "LawFilled",
|
||||
"description": "",
|
||||
"created_at": "2021-01-11T13:37:55.330Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "Left",
|
||||
@@ -1551,37 +1551,37 @@
|
||||
"name": "Locked",
|
||||
"description": "",
|
||||
"created_at": "2020-07-01T10:37:43.027Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "LockedFilled",
|
||||
"description": "",
|
||||
"created_at": "2021-01-11T13:37:55.325Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "Login",
|
||||
"description": "",
|
||||
"created_at": "2020-07-01T10:37:43.087Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "LoginFilled",
|
||||
"description": "",
|
||||
"created_at": "2021-01-11T13:37:55.531Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "Logout",
|
||||
"description": "",
|
||||
"created_at": "2020-07-01T10:37:42.931Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "LogoutFilled",
|
||||
"description": "",
|
||||
"created_at": "2021-01-11T13:37:55.609Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "Male",
|
||||
@@ -1811,6 +1811,12 @@
|
||||
"created_at": "2021-12-15T15:16:55.869Z",
|
||||
"pageName": "People"
|
||||
},
|
||||
{
|
||||
"name": "Paragraph",
|
||||
"description": "",
|
||||
"created_at": "2023-01-17T13:24:22.989Z",
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "Parking",
|
||||
"description": "",
|
||||
@@ -1827,13 +1833,13 @@
|
||||
"name": "Passport",
|
||||
"description": "",
|
||||
"created_at": "2020-07-01T10:37:43.078Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "PassportFilled",
|
||||
"description": "",
|
||||
"created_at": "2021-01-11T13:37:55.757Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "PensionBag",
|
||||
@@ -2615,6 +2621,12 @@
|
||||
"created_at": "2021-01-28T14:00:38.585Z",
|
||||
"pageName": "Buildings"
|
||||
},
|
||||
{
|
||||
"name": "Translate",
|
||||
"description": "",
|
||||
"created_at": "2023-01-18T10:52:22.311Z",
|
||||
"pageName": "Interface"
|
||||
},
|
||||
{
|
||||
"name": "Truck",
|
||||
"description": "",
|
||||
@@ -2643,13 +2655,13 @@
|
||||
"name": "Unlocked",
|
||||
"description": "",
|
||||
"created_at": "2020-07-01T10:37:42.823Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "UnlockedFilled",
|
||||
"description": "",
|
||||
"created_at": "2021-01-11T13:37:55.317Z",
|
||||
"pageName": "Privacy and secutity"
|
||||
"pageName": "Privacy and security"
|
||||
},
|
||||
{
|
||||
"name": "Up",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-icons",
|
||||
"version": "2.0.11",
|
||||
"version": "2.1.1",
|
||||
"description": "Aksel icons for NAV",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"repository": {
|
||||
@@ -32,7 +32,7 @@
|
||||
"create-package-json-pointers-to-esm": "node ../../scripts/createPackageJsonsWithESMPointers.js",
|
||||
"create-icons": "svgr --silent --out-dir src svg",
|
||||
"clean": "rimraf src cjs esm",
|
||||
"build": "copyfiles util/* src && yarn create-icons && concurrently \"tsc\" \"tsc -p tsconfig.esm.json\" && yarn create-package-json-pointers-to-esm",
|
||||
"build": "copyfiles util/* src && yarn create-icons && concurrently \"tsc\" \"tsc -p tsconfig.esm.json\" && node cleanTypes.js && yarn create-package-json-pointers-to-esm",
|
||||
"update": "ts-node figma/index.ts dotenv/config",
|
||||
"test": "jest"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0ZM12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM13 6V11H18V13H13V18H11V13H6V11H11V6H13Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.75 2.5C6.64137 2.5 2.5 6.64137 2.5 11.75C2.5 16.8586 6.64137 21 11.75 21C16.8586 21 21 16.8586 21 11.75C21 6.64137 16.8586 2.5 11.75 2.5ZM1 11.75C1 5.81294 5.81294 1 11.75 1C17.6871 1 22.5 5.81294 22.5 11.75C22.5 17.6871 17.6871 22.5 11.75 22.5C5.81294 22.5 1 17.6871 1 11.75Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17 12.75H7V11.25H17V12.75Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 17L11.25 7L12.75 7L12.75 17H11.25Z" fill="#262626"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 655 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0ZM13 6V11H18V13H13V18H11V13H6V11H11V6H13Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.75 1C5.81294 1 1 5.81294 1 11.75C1 17.6871 5.81294 22.5 11.75 22.5C17.6871 22.5 22.5 17.6871 22.5 11.75C22.5 5.81294 17.6871 1 11.75 1ZM11.25 12.75V17H12.75V12.75H17V11.25H12.75V7H11.25V11.25H7V12.75H11.25Z" fill="#262626"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 323 B After Width: | Height: | Size: 381 B |
@@ -1,3 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 6.81465 5.94668 2.5511 11 2.04938V12.4142L18.3288 19.743C16.6044 21.1544 14.4017 22 12 22C6.47715 22 2 17.5228 2 12ZM19.7488 21.163C17.6587 22.9321 14.9533 24 12 24C5.37258 24 0 18.6274 0 12C0 5.70934 4.84047 0.549225 11 0.0410738V0H12H13V0.0410738C19.1595 0.549225 24 5.70934 24 12C24 14.9533 22.9321 17.6587 21.163 19.7488L21.1924 19.7782L20.4853 20.4853L19.7782 21.1924L19.7488 21.163ZM19.743 18.3288C21.1543 16.6044 22 14.4017 22 12C22 6.81465 18.0533 2.5511 13 2.04938V11.5858L19.743 18.3288Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75ZM1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 2.5H12.75V12.1729L19.5103 18.4504L18.4897 19.5496L11.25 12.8271V2.5Z" fill="#262626"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 676 B After Width: | Height: | Size: 616 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 12V0.0410767C4.84047 0.549228 0 5.70934 0 12C0 18.6274 5.37258 24 12 24C14.9922 24 17.7286 22.9048 19.8303 21.0935L11 12ZM24 12C24 12.9017 23.9005 13.7803 23.712 14.6252C23.297 16.4847 22.4505 18.1814 21.2831 19.6048L13 11V0.0410767C18.2766 0.476386 22.5852 4.32557 23.712 9.37485C23.9005 10.2198 24 11.0983 24 12Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 1.27576C5.66285 1.6608 1.25 6.31506 1.25 12C1.25 17.9371 6.06294 22.75 12 22.75C14.5711 22.75 16.9313 21.8474 18.7812 20.3418L11.25 12.8107V1.27576ZM19.8766 19.3159C21.6596 17.3972 22.75 14.8259 22.75 12C22.75 6.31506 18.3372 1.6608 12.75 1.27576V12.1893L19.8766 19.3159Z" fill="#262626"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 449 B |
@@ -1,3 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0ZM12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM18 11H6V13H18V11Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.75 2.5C6.64137 2.5 2.5 6.64137 2.5 11.75C2.5 16.8586 6.64137 21 11.75 21C16.8586 21 21 16.8586 21 11.75C21 6.64137 16.8586 2.5 11.75 2.5ZM1 11.75C1 5.81294 5.81294 1 11.75 1C17.6871 1 22.5 5.81294 22.5 11.75C22.5 17.6871 17.6871 22.5 11.75 22.5C5.81294 22.5 1 17.6871 1 11.75Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17 12.75H7V11.25H17V12.75Z" fill="#262626"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 413 B After Width: | Height: | Size: 546 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C18.6274 24 24 18.6274 24 12ZM18 11H6V13H18V11Z" fill="#262626"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.75 1C5.81294 1 1 5.81294 1 11.75C1 17.6871 5.81294 22.5 11.75 22.5C17.6871 22.5 22.5 17.6871 22.5 11.75C22.5 5.81294 17.6871 1 11.75 1ZM7 12.75H17V11.25H7V12.75Z" fill="#262626"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 336 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C9.79085 2 7.99999 3.79086 7.99999 6C7.99999 6.66187 8.16311 7.29714 8.45828 7.85918C6.00646 9.12956 5.6173 13.1739 8.76271 14.4845L12.7692 16.1538C13.5145 16.4644 14 17.1926 14 18C14 19.1046 13.1046 20 12 20C10.8954 20 9.99999 19.1046 9.99999 18H7.99999C7.99999 20.2091 9.79085 22 12 22C14.2091 22 16 20.2091 16 18C16 17.3381 15.8369 16.7029 15.5417 16.1408C17.9935 14.8704 18.3827 10.8261 15.2373 9.51554L11.2308 7.84615C10.4855 7.53561 9.99999 6.8074 9.99999 6C9.99999 4.89543 10.8954 4 12 4C13.1046 4 14 4.89543 14 6H16C16 3.79086 14.2091 2 12 2ZM13.9738 14.5208C15.6075 14.4232 16.1245 12.0519 14.4681 11.3617L10.4615 9.69231C10.311 9.62957 10.1657 9.55832 10.0262 9.47924C8.39252 9.57681 7.87544 11.9481 9.53194 12.6383L13.5385 14.3077C13.689 14.3704 13.8343 14.4417 13.9738 14.5208Z" fill="#262626"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 965 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.5 19.5L15.5 10.5L19.5 19.5" stroke="#262626" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13 17H18" stroke="#262626" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<mask id="mask0_6814_1365" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="2" y="4" width="15" height="17">
|
||||
<path d="M9.90083 18.7893L13.9008 9.78926C14.1817 9.15728 14.8084 8.75 15.5 8.75C16.1193 8.75 16.6865 9.07657 17.0001 9.59861V4H2.00005V21H10.5983C9.85068 20.5519 9.53784 19.606 9.90083 18.7893Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_6814_1365)">
|
||||
<path d="M4 7.5H16" stroke="#262626" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 7.5V5.5" stroke="#262626" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13.5 7.5L13.4116 7.78744C12.2005 11.7232 9.30115 14.9162 5.5 16.5" stroke="#262626" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.5 7.5L6.58844 7.78744C7.79946 11.7232 10.6989 14.9162 14.5 16.5" stroke="#262626" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,5 +1,28 @@
|
||||
# @navikt/ds-css-internal
|
||||
|
||||
## 2.1.1
|
||||
|
||||
## 2.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1665](https://github.com/navikt/Designsystemet/pull/1665) [`6ab84e581`](https://github.com/navikt/Designsystemet/commit/6ab84e581f8d3e850bcec340608354335506a051) Thanks [@KenAJoh](https://github.com/KenAJoh)! - :sparkles: Ny Timeline-komponent for interne-flater
|
||||
:bug: Fikset feil danger-hover token
|
||||
|
||||
## 2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
## 2.0.16
|
||||
|
||||
## 2.0.15
|
||||
|
||||
## 2.0.14
|
||||
|
||||
## 2.0.13
|
||||
|
||||
## 2.0.12
|
||||
|
||||
## 2.0.11
|
||||
|
||||
## 2.0.10
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import "copy-to-clipboard.css";
|
||||
@import "dropdown.css";
|
||||
@import "header.css";
|
||||
@import "timeline.css";
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css-internal",
|
||||
"version": "2.0.11",
|
||||
"version": "2.1.1",
|
||||
"description": "CSS for NAV internal Designsystem-components",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
.navdsi-timeline {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
width: 100%;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.navdsi-timeline__axislabels {
|
||||
position: relative;
|
||||
height: 1rem;
|
||||
box-sizing: content-box;
|
||||
grid-column: 2;
|
||||
margin-bottom: var(--a-spacing-4);
|
||||
margin-left: var(--a-spacing-8);
|
||||
}
|
||||
|
||||
.navdsi-timeline__row-label {
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
margin-right: var(--a-spacing-4);
|
||||
grid-column: 1;
|
||||
height: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navdsi-timeline__axislabels-label {
|
||||
position: absolute;
|
||||
color: var(--ac-timeline-axislabel-text, var(--a-text-subtle));
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.navdsi-timeline__row {
|
||||
display: flex;
|
||||
background: var(--ac-timeline-row-bg, var(--a-surface-subtle));
|
||||
margin: 0 0 var(--a-spacing-8);
|
||||
padding-left: var(--a-spacing-8);
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.navdsi-timeline__row--active {
|
||||
background: var(--ac-timeline-row-active-bg, var(--a-surface-selected));
|
||||
}
|
||||
|
||||
.navdsi-timeline__row:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.navdsi-timeline__row-periods {
|
||||
height: 1.5rem;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.navdsi-timeline__row-periods > li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.navdsi-timeline__period {
|
||||
height: 100%;
|
||||
border-radius: var(--a-border-radius-full);
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 2;
|
||||
border: none;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--inner {
|
||||
margin: 0 var(--a-spacing-1) 0 var(--a-spacing-2);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: clip;
|
||||
text-align: left;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
/* .navdsi-timeline__period > * {
|
||||
flex-shrink: 0;
|
||||
} */
|
||||
|
||||
.navdsi-timeline__period--clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--success {
|
||||
background: var(--ac-timeline-period-success-bg, var(--a-surface-success-subtle));
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-period-success-border, var(--a-border-success));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--success.navdsi-timeline__period--clickable:hover {
|
||||
background: var(--a-surface-success-subtle-hover);
|
||||
background: var(--ac-timeline-period-success-bg-hover, var(--a-surface-success-subtle-hover));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--warning {
|
||||
background: var(--ac-timeline-period-warning-bg, var(--a-surface-warning-subtle));
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-period-warning-border, var(--a-border-warning));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--warning.navdsi-timeline__period--clickable:hover {
|
||||
background: var(--ac-timeline-period-warning-bg-hover, var(--a-surface-warning-subtle-hover));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--danger {
|
||||
background: var(--ac-timeline-period-danger-bg, var(--a-surface-danger-subtle));
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-period-danger-border, var(--a-border-danger));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--danger.navdsi-timeline__period--clickable:hover {
|
||||
background: var(--ac-timeline-period-danger-bg-hover, var(--a-surface-danger-subtle-hover));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--info {
|
||||
background: var(--ac-timeline-period-info-bg, var(--a-surface-info-subtle));
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-period-info-border, var(--a-border-info));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--info.navdsi-timeline__period--clickable:hover {
|
||||
background: var(--ac-timeline-period-info-bg-hover, var(--a-surface-info-subtle-hover));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--neutral {
|
||||
background: var(--ac-timeline-period-neutral-bg, var(--a-bg-subtle));
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-period-neutral-border, var(--a-border-default));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--neutral.navdsi-timeline__period--clickable:hover {
|
||||
background: var(--ac-timeline-period-neutral-bg-hover, var(--a-gray-200));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--connected-both {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--connected-right {
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--connected-left {
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--selected {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-timeline-period-selected-border, var(--a-border-action-selected));
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.navdsi-timeline__pin-wrapper {
|
||||
position: absolute;
|
||||
grid-column: 2;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
isolation: isolate;
|
||||
|
||||
--navdsc-timeline-pin-size: 0.9rem;
|
||||
}
|
||||
|
||||
.navdsi-timeline__pin-button {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background: var(--a-surface-default);
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-pin-bg, var(--a-surface-danger)),
|
||||
0 0 0 4px var(--ac-timeline-pin-bg, var(--a-surface-danger)), 0 0 0 5px var(--a-surface-default);
|
||||
z-index: 2;
|
||||
border-radius: var(--a-border-radius-full);
|
||||
padding: 3.5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.navdsi-timeline__pin-button::before {
|
||||
content: "";
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: var(--a-border-radius-full);
|
||||
left: calc(-12.5px + 3.5px);
|
||||
top: calc(-12.5px + 3.5px);
|
||||
}
|
||||
|
||||
.navdsi-timeline__pin-button:focus {
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-pin-bg, var(--a-surface-danger)),
|
||||
0 0 0 3px var(--ac-timeline-pin-bg, var(--a-surface-danger)), 0 0 0 4px var(--a-surface-default),
|
||||
0 0 0 6px var(--a-border-focus);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.navdsi-timeline__pin-button:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-pin-bg-hover, var(--a-surface-danger-hover)),
|
||||
0 0 0 4px var(--ac-timeline-pin-bg-hover, var(--a-surface-danger-hover)), 0 0 0 5px var(--a-surface-default);
|
||||
}
|
||||
|
||||
.navdsi-timeline__pin-button:hover:focus {
|
||||
box-shadow: inset 0 0 0 1px var(--ac-timeline-pin-bg-hover, var(--a-surface-danger-hover)),
|
||||
0 0 0 3px var(--ac-timeline-pin-bg-hover, var(--a-surface-danger-hover)), 0 0 0 4px var(--a-surface-default),
|
||||
0 0 0 6px var(--a-border-focus);
|
||||
}
|
||||
|
||||
.navdsi-timeline__pin-wrapper::before {
|
||||
content: "";
|
||||
top: var(--navdsc-timeline-pin-size);
|
||||
height: calc(94% - var(--navdsc-timeline-pin-size));
|
||||
width: 1px;
|
||||
margin: 0 auto;
|
||||
background: var(--a-surface-inverted);
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom {
|
||||
margin-top: var(--a-spacing-6);
|
||||
float: right;
|
||||
display: flex;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom-button {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
padding: 6px 9px 6px 8px;
|
||||
border: 1px solid var(--ac-timeline-zoom-border, var(--a-border-default));
|
||||
border-width: 1px 0 1px 1px;
|
||||
background: var(--ac-timeline-zoom-bg, var(--a-surface-default));
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom li:first-child .navdsi-timeline__zoom-button {
|
||||
border-radius: var(--a-border-radius-medium) 0 0 var(--a-border-radius-medium);
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom li:last-child .navdsi-timeline__zoom-button {
|
||||
border-width: 1px;
|
||||
border-radius: 0 var(--a-border-radius-medium) var(--a-border-radius-medium) 0;
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom li:only-child .navdsi-timeline__zoom-button {
|
||||
border-radius: var(--a-border-radius-medium);
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom-button:not([aria-pressed="true"]):hover {
|
||||
background: var(--ac-timeline-zoom-hover, var(--a-surface-action-subtle-hover));
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom-button[aria-pressed="true"] {
|
||||
background: var(--ac-timeline-zoom-selected-bg, var(--a-surface-inverted));
|
||||
color: var(--ac-timeline-zoom-selected-text, var(--a-text-on-inverted));
|
||||
}
|
||||
|
||||
.navdsi-timeline__period:focus {
|
||||
outline: none;
|
||||
z-index: 4;
|
||||
box-shadow: 0 0 0 2px var(--a-border-focus);
|
||||
}
|
||||
|
||||
.navdsi-timeline__period--selected:focus {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-timeline-period-selected-border, var(--a-border-action-selected)),
|
||||
0 0 0 2px var(--a-border-focus);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom li:focus-within {
|
||||
z-index: var(--a-z-index-focus);
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom-button:focus {
|
||||
box-shadow: 0 0 0 1px var(--a-surface-default), 0 0 0 3px var(--a-border-focus);
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.navdsi-timeline__zoom li:not(:last-child) .navdsi-timeline__zoom-button:focus {
|
||||
margin-right: 2px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
@@ -16,5 +16,33 @@
|
||||
"--ac-header-text": "--a-text-on-inverted",
|
||||
"--ac-header-hover-bg": "--a-surface-inverted-hover",
|
||||
"--ac-header-active-bg": "--a-surface-inverted-active"
|
||||
},
|
||||
"timeline": {
|
||||
"--ac-timeline-row-bg": "--a-surface-subtle",
|
||||
"--ac-timeline-row-active-bg": "--a-surface-selected",
|
||||
"--ac-timeline-period-success-bg": "--a-surface-success-subtle",
|
||||
"--ac-timeline-period-success-border": "--a-border-success",
|
||||
"--ac-timeline-period-success-bg-hover": "--a-surface-success-subtle-hover",
|
||||
"--ac-timeline-period-warning-bg": "--a-surface-warning-subtle",
|
||||
"--ac-timeline-period-warning-border": "--a-border-warning",
|
||||
"--ac-timeline-period-warning-bg-hover": "--a-surface-warning-subtle-hover",
|
||||
"--ac-timeline-period-danger-bg": "--a-surface-danger-subtle",
|
||||
"--ac-timeline-period-danger-border": "--a-border-danger",
|
||||
"--ac-timeline-period-danger-bg-hover": "--a-surface-danger-subtle-hover",
|
||||
"--ac-timeline-period-info-bg": "--a-surface-info-subtle",
|
||||
"--ac-timeline-period-info-border": "--a-border-info",
|
||||
"--ac-timeline-period-info-bg-hover": "--a-surface-info-subtle-hover",
|
||||
"--ac-timeline-period-neutral-bg": "--a-bg-subtle",
|
||||
"--ac-timeline-period-neutral-border": "--a-border-default",
|
||||
"--ac-timeline-period-neutral-bg-hover": "--a-gray-200",
|
||||
"--ac-timeline-period-selected-border": "--a-border-action-selected",
|
||||
"--ac-timeline-pin-bg": "--a-surface-danger",
|
||||
"--ac-timeline-pin-bg-hover": "--a-surface-danger-hover",
|
||||
"--ac-timeline-zoom-border": "--a-border-default",
|
||||
"--ac-timeline-zoom-bg": "--a-surface-default",
|
||||
"--ac-timeline-zoom-hover": "--a-surface-action-subtle-hover",
|
||||
"--ac-timeline-zoom-selected-bg": "--a-surface-inverted",
|
||||
"--ac-timeline-zoom-selected-text": "--a-text-on-inverted",
|
||||
"--ac-timeline-axislabel-text": "--a-text-subtle"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,82 @@
|
||||
# @navikt/ds-react-internal
|
||||
|
||||
## 2.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`dcf5cfc06`](https://github.com/navikt/Designsystemet/commit/dcf5cfc06b751341559ef30085bd974820531e57)]:
|
||||
- @navikt/ds-react@2.1.1
|
||||
- @navikt/ds-icons@2.1.1
|
||||
|
||||
## 2.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1665](https://github.com/navikt/Designsystemet/pull/1665) [`6ab84e581`](https://github.com/navikt/Designsystemet/commit/6ab84e581f8d3e850bcec340608354335506a051) Thanks [@KenAJoh](https://github.com/KenAJoh)! - :sparkles: Ny Timeline-komponent for interne-flater
|
||||
:bug: Fikset feil danger-hover token
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`0873fe652`](https://github.com/navikt/Designsystemet/commit/0873fe6520e817e462197c162b49c05826da4838)]:
|
||||
- @navikt/ds-react@2.1.0
|
||||
- @navikt/ds-icons@2.1.0
|
||||
|
||||
## 2.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`98177d0cd`](https://github.com/navikt/Designsystemet/commit/98177d0cd3802029dcf398ce93c1767d3b0860c5)]:
|
||||
- @navikt/ds-react@2.0.18
|
||||
- @navikt/ds-icons@2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`27e3dfee0`](https://github.com/navikt/Designsystemet/commit/27e3dfee07e135893e9f62cd32f80ed83a3510de)]:
|
||||
- @navikt/ds-icons@2.0.17
|
||||
- @navikt/ds-react@2.0.17
|
||||
|
||||
## 2.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`54f8b0795`](https://github.com/navikt/Designsystemet/commit/54f8b07956bbfb7de51f6682a5fc64903bfd1bdf)]:
|
||||
- @navikt/ds-icons@2.0.16
|
||||
- @navikt/ds-react@2.0.16
|
||||
|
||||
## 2.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`22dfd8e60`](https://github.com/navikt/Designsystemet/commit/22dfd8e60315c421749394d35e41f412a00a7593)]:
|
||||
- @navikt/ds-react@2.0.15
|
||||
- @navikt/ds-icons@2.0.15
|
||||
|
||||
## 2.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`8e5c55443`](https://github.com/navikt/Designsystemet/commit/8e5c554431c98b1b658c5054c0b3cf08b8a65e52)]:
|
||||
- @navikt/ds-react@2.0.14
|
||||
- @navikt/ds-icons@2.0.14
|
||||
|
||||
## 2.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`a6c339123`](https://github.com/navikt/Designsystemet/commit/a6c3391237f13394063e4c004ac9efb1875fc944)]:
|
||||
- @navikt/ds-icons@2.0.13
|
||||
- @navikt/ds-react@2.0.13
|
||||
|
||||
## 2.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-react@2.0.12
|
||||
- @navikt/ds-icons@2.0.12
|
||||
|
||||
## 2.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react-internal",
|
||||
"version": "2.0.11",
|
||||
"version": "2.1.1",
|
||||
"description": "Aksel react components for internal UIs in NAV",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -35,8 +35,8 @@
|
||||
"watch": "tsc --watch -p tsconfig.esm.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navikt/ds-icons": "^2.0.11",
|
||||
"@navikt/ds-react": "^2.0.11",
|
||||
"@navikt/ds-icons": "^2.1.1",
|
||||
"@navikt/ds-react": "^2.1.1",
|
||||
"clsx": "^1.1.1",
|
||||
"copy-to-clipboard": "^3.3.1"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./copy-to-clipboard";
|
||||
export * from "./dropdown";
|
||||
export * from "./header";
|
||||
export * from "./timeline";
|
||||
@@ -0,0 +1,142 @@
|
||||
import { Detail } from "@navikt/ds-react";
|
||||
import {
|
||||
addDays,
|
||||
addMonths,
|
||||
addYears,
|
||||
differenceInDays,
|
||||
differenceInMonths,
|
||||
differenceInYears,
|
||||
endOfMonth,
|
||||
endOfYear,
|
||||
format,
|
||||
startOfDay,
|
||||
startOfMonth,
|
||||
startOfYear,
|
||||
subDays,
|
||||
} from "date-fns";
|
||||
import React from "react";
|
||||
import { useTimelineContext } from "./hooks/useTimelineContext";
|
||||
import { isVisible } from "./utils";
|
||||
import { horizontalPositionAndWidth } from "./utils/calc";
|
||||
import { AxisLabel } from "./utils/types.external";
|
||||
|
||||
export const dayLabels = (
|
||||
start: Date,
|
||||
end: Date,
|
||||
totalDays: number,
|
||||
direction: "left" | "right"
|
||||
): AxisLabel[] => {
|
||||
const increment = Math.ceil(totalDays / 10);
|
||||
const lastDay = startOfDay(end);
|
||||
return new Array(totalDays)
|
||||
.fill(lastDay)
|
||||
.map((thisDay, i) => {
|
||||
if (i % increment !== 0) return null;
|
||||
const day: Date = subDays(thisDay, i);
|
||||
const { horizontalPosition, width } = horizontalPositionAndWidth(
|
||||
day,
|
||||
addDays(day, 1),
|
||||
start,
|
||||
end
|
||||
);
|
||||
return {
|
||||
direction: direction,
|
||||
horizontalPosition: horizontalPosition,
|
||||
label: format(day, "MM.dd"),
|
||||
date: day,
|
||||
width: width,
|
||||
};
|
||||
})
|
||||
.filter((label) => label !== null) as AxisLabel[];
|
||||
};
|
||||
|
||||
export const monthLabels = (
|
||||
start: Date,
|
||||
end: Date,
|
||||
direction: "left" | "right"
|
||||
): AxisLabel[] => {
|
||||
const startMonth = startOfMonth(start);
|
||||
const endMonth = endOfMonth(end);
|
||||
const numberOfMonths = differenceInMonths(endMonth, startMonth) + 1;
|
||||
return new Array(numberOfMonths).fill(startMonth).map((thisMonth, i) => {
|
||||
const month: Date = addMonths(thisMonth, i);
|
||||
const { horizontalPosition, width } = horizontalPositionAndWidth(
|
||||
month,
|
||||
addMonths(month, 1),
|
||||
start,
|
||||
end
|
||||
);
|
||||
return {
|
||||
direction: direction,
|
||||
horizontalPosition: horizontalPosition,
|
||||
label: format(month, "MMM yy"),
|
||||
date: month,
|
||||
width: width,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const yearLabels = (
|
||||
start: Date,
|
||||
end: Date,
|
||||
direction: "left" | "right"
|
||||
): AxisLabel[] => {
|
||||
const firstYear = startOfYear(start);
|
||||
const lastYear = endOfYear(end);
|
||||
const yearCount = differenceInYears(lastYear, start) + 1;
|
||||
return new Array(yearCount).fill(firstYear).map((thisYear, i) => {
|
||||
const year: Date = addYears(thisYear, i);
|
||||
const { horizontalPosition, width } = horizontalPositionAndWidth(
|
||||
year,
|
||||
addYears(year, 1),
|
||||
start,
|
||||
end
|
||||
);
|
||||
return {
|
||||
direction: direction,
|
||||
horizontalPosition: horizontalPosition,
|
||||
label: year.getFullYear().toString(),
|
||||
date: year,
|
||||
width: width,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const axisLabels = (
|
||||
start: Date,
|
||||
end: Date,
|
||||
direction: "left" | "right"
|
||||
): AxisLabel[] => {
|
||||
const totalDays = differenceInDays(end, start);
|
||||
if (totalDays < 40) {
|
||||
return dayLabels(start, end, totalDays, direction);
|
||||
} else if (totalDays < 370) {
|
||||
return monthLabels(start, end, direction);
|
||||
} else {
|
||||
return yearLabels(start, end, direction);
|
||||
}
|
||||
};
|
||||
|
||||
export const AxisLabels = () => {
|
||||
const { endDate, startDate, direction } = useTimelineContext();
|
||||
const labels = axisLabels(startDate, endDate, direction).filter(isVisible);
|
||||
|
||||
return (
|
||||
<div className="navdsi-timeline__axislabels" aria-hidden="true">
|
||||
{labels.map((etikett) => (
|
||||
<Detail
|
||||
className="navdsi-timeline__axislabels-label"
|
||||
as="div"
|
||||
key={etikett.label}
|
||||
style={{
|
||||
justifyContent: direction === "left" ? "flex-start" : "flex-end",
|
||||
[direction]: `${etikett.horizontalPosition}%`,
|
||||
width: `${etikett.width}%`,
|
||||
}}
|
||||
>
|
||||
{etikett.label}
|
||||
</Detail>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
import { mergeRefs, Popover } from "@navikt/ds-react";
|
||||
import { format } from "date-fns";
|
||||
import React, { forwardRef, useMemo, useRef, useState } from "react";
|
||||
import { useTimelineContext } from "./hooks/useTimelineContext";
|
||||
import { position } from "./utils/calc";
|
||||
import { TimelineComponentTypes } from "./utils/types.internal";
|
||||
|
||||
export interface TimelinePinProps {
|
||||
/**
|
||||
* Date position for the pin.
|
||||
*/
|
||||
date: Date;
|
||||
/**
|
||||
* Content in Pin Popover.
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface PinType
|
||||
extends React.ForwardRefExoticComponent<
|
||||
TimelinePinProps & React.RefAttributes<HTMLButtonElement>
|
||||
> {
|
||||
componentType: TimelineComponentTypes;
|
||||
}
|
||||
|
||||
export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
|
||||
({ date, children, ...rest }, ref) => {
|
||||
const { startDate, endDate, direction } = useTimelineContext();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
||||
const mergedRef = useMemo(() => mergeRefs([buttonRef, ref]), [ref]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="navdsi-timeline__pin-wrapper"
|
||||
style={{ [direction]: `${position(date, startDate, endDate)}%` }}
|
||||
>
|
||||
<button
|
||||
{...rest}
|
||||
ref={mergedRef}
|
||||
className="navdsi-timeline__pin-button"
|
||||
onClick={() => setOpen(!open)}
|
||||
aria-label={`pin:${format(date, "dd.MM.yyyy")}`}
|
||||
aria-expanded={open}
|
||||
/>
|
||||
</div>
|
||||
{children && (
|
||||
<Popover
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
anchorEl={buttonRef.current}
|
||||
offset={18}
|
||||
>
|
||||
<Popover.Content>{children}</Popover.Content>
|
||||
</Popover>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
) as PinType;
|
||||
|
||||
Pin.componentType = "pin";
|
||||
|
||||
export default Pin;
|
||||
@@ -0,0 +1,209 @@
|
||||
import { endOfDay, isSameDay, startOfDay } from "date-fns";
|
||||
import React, { forwardRef, useMemo, useRef, useState } from "react";
|
||||
import { AxisLabels } from "./AxisLabels";
|
||||
import { RowContext } from "./hooks/useRowContext";
|
||||
import { TimelineContext } from "./hooks/useTimelineContext";
|
||||
import {
|
||||
useEarliestDate,
|
||||
useLatestDate,
|
||||
useTimelineRows,
|
||||
} from "./hooks/useTimelineRows";
|
||||
import Period, { PeriodType } from "./period";
|
||||
import Pin, { PinType } from "./Pin";
|
||||
import TimelineRow, { TimelineRowType } from "./TimelineRow";
|
||||
import { parseRows } from "./utils/timeline";
|
||||
import Zoom, { ZoomType } from "./zoom";
|
||||
|
||||
export interface TimelineProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Decides startingpoint in timeline.
|
||||
* Defaults to earliest date among the timeline periods.
|
||||
*/
|
||||
startDate?: Date;
|
||||
/**
|
||||
* Decides end-date for timeline.
|
||||
* Defaults to the latest date among the timeline periods.
|
||||
*/
|
||||
endDate?: Date;
|
||||
/**
|
||||
* Decides direction which periods are sorted/displayed. "left" ascends from earliest date on left.
|
||||
* @default "left"
|
||||
*/
|
||||
direction?: "left" | "right";
|
||||
}
|
||||
|
||||
interface TimelineComponent
|
||||
extends React.ForwardRefExoticComponent<TimelineProps> {
|
||||
/**
|
||||
* Built-in timeline row
|
||||
*/
|
||||
Row: TimelineRowType;
|
||||
/**
|
||||
* Built-in row period
|
||||
*/
|
||||
Period: PeriodType;
|
||||
/**
|
||||
* Built-in timeline pin
|
||||
*/
|
||||
Pin: PinType;
|
||||
/**
|
||||
* Built-in timeline zoom-component
|
||||
*/
|
||||
Zoom: ZoomType;
|
||||
}
|
||||
|
||||
export const Timeline = forwardRef<HTMLDivElement, TimelineProps>(
|
||||
({ children, startDate, endDate, direction = "left", ...rest }, ref) => {
|
||||
const isMultipleRows = Array.isArray(children);
|
||||
|
||||
const firstFocusabled = useRef<
|
||||
{ ref: HTMLButtonElement | null; id: number }[]
|
||||
>([]);
|
||||
|
||||
if (!isMultipleRows) {
|
||||
children = [children];
|
||||
}
|
||||
const rowChildren = React.Children.toArray(children).filter(
|
||||
(c: any) => c?.type?.componentType === "row"
|
||||
);
|
||||
|
||||
const pins = React.Children.toArray(children).filter(
|
||||
(c: any) => c?.type?.componentType === "pin"
|
||||
);
|
||||
|
||||
const zoomComponent = React.Children.toArray(children).find(
|
||||
(c: any) => c?.type?.componentType === "zoom"
|
||||
);
|
||||
|
||||
const rowsRaw = useMemo(() => {
|
||||
return parseRows(rowChildren);
|
||||
}, [rowChildren]);
|
||||
|
||||
const rows = rowsRaw.map((r) => {
|
||||
if (r?.periods) {
|
||||
return r.periods;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
const initialStartDate = startOfDay(useEarliestDate({ startDate, rows }));
|
||||
const [start, setStart] = useState(initialStartDate);
|
||||
const [activeRow, setActiveRow] = useState<number | null>(null);
|
||||
const [endInclusive, setEndInclusive] = useState(
|
||||
endOfDay(useLatestDate({ endDate, rows }))
|
||||
);
|
||||
|
||||
const initialEndDate = endOfDay(useLatestDate({ endDate, rows }));
|
||||
const processedRows = useTimelineRows(
|
||||
rowsRaw,
|
||||
start,
|
||||
endInclusive,
|
||||
direction
|
||||
);
|
||||
|
||||
const handleZoomChange = (zoomStart: Date) => {
|
||||
if (direction === "left") {
|
||||
if (isSameDay(zoomStart, start)) {
|
||||
setStart(initialStartDate);
|
||||
return;
|
||||
}
|
||||
setStart(zoomStart);
|
||||
} else {
|
||||
if (isSameDay(zoomStart, endInclusive)) {
|
||||
setEndInclusive(initialEndDate);
|
||||
return;
|
||||
}
|
||||
setEndInclusive(zoomStart);
|
||||
}
|
||||
};
|
||||
|
||||
const handleActiveRowChange = (key: string) => {
|
||||
if (activeRow !== null && key === "ArrowDown") {
|
||||
for (let i = activeRow + 1; i < processedRows.length; i++) {
|
||||
const row = processedRows[i];
|
||||
if (row.periods.find((p) => !!p.children || !!p.onSelectPeriod)) {
|
||||
setActiveRow(i);
|
||||
firstFocusabled.current.find((x) => x.id === i)?.ref?.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (activeRow !== null && key === "ArrowUp") {
|
||||
for (let i = activeRow - 1; i >= 0; i--) {
|
||||
const row = processedRows[i];
|
||||
if (row.periods.find((p) => !!p.children || !!p.onSelectPeriod)) {
|
||||
setActiveRow(i);
|
||||
firstFocusabled.current.find((x) => x.id === i)?.ref?.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const addFocusable = (ref: HTMLButtonElement | null, id: number) => {
|
||||
let items = firstFocusabled.current;
|
||||
items = items.filter((x) => x.id !== id);
|
||||
items.push({ ref, id });
|
||||
firstFocusabled.current = items;
|
||||
};
|
||||
|
||||
return (
|
||||
<TimelineContext.Provider
|
||||
value={{
|
||||
startDate: start,
|
||||
endDate: endInclusive,
|
||||
direction: direction,
|
||||
setStart: (d) => handleZoomChange(d),
|
||||
setEndInclusive: (d) => setEndInclusive(d),
|
||||
activeRow: activeRow,
|
||||
setActiveRow: (key) => handleActiveRowChange(key),
|
||||
initiate: (i) => setActiveRow(i),
|
||||
addFocusable,
|
||||
}}
|
||||
>
|
||||
<div {...rest} ref={ref}>
|
||||
<div className="navdsi-timeline">
|
||||
<AxisLabels />
|
||||
|
||||
{pins.map((pin) => {
|
||||
return pin;
|
||||
})}
|
||||
|
||||
{processedRows.map((row, i) => {
|
||||
return (
|
||||
<RowContext.Provider
|
||||
key={`row-${row.id}`}
|
||||
value={{
|
||||
periods: row.periods,
|
||||
id: row.id,
|
||||
active: activeRow === i,
|
||||
index: i,
|
||||
}}
|
||||
>
|
||||
<TimelineRow
|
||||
{...row?.restProps}
|
||||
ref={row?.ref}
|
||||
label={row.label}
|
||||
icon={row.icon}
|
||||
headingTag={row.headingTag}
|
||||
/>
|
||||
</RowContext.Provider>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{zoomComponent && zoomComponent}
|
||||
</div>
|
||||
</TimelineContext.Provider>
|
||||
);
|
||||
}
|
||||
) as TimelineComponent;
|
||||
|
||||
Timeline.Row = TimelineRow;
|
||||
Timeline.Period = Period;
|
||||
Timeline.Pin = Pin;
|
||||
Timeline.Zoom = Zoom;
|
||||
|
||||
export default Timeline;
|
||||
@@ -0,0 +1,122 @@
|
||||
import { BodyShort } from "@navikt/ds-react";
|
||||
import { format } from "date-fns";
|
||||
import React, { forwardRef } from "react";
|
||||
import { PeriodContext } from "./hooks/usePeriodContext";
|
||||
import { useRowContext } from "./hooks/useRowContext";
|
||||
import { useTimelineContext } from "./hooks/useTimelineContext";
|
||||
import Period from "./period";
|
||||
import {
|
||||
PositionedPeriod,
|
||||
TimelineComponentTypes,
|
||||
} from "./utils/types.internal";
|
||||
import cl from "clsx";
|
||||
|
||||
export interface TimelineRowProps
|
||||
extends React.HTMLAttributes<HTMLOListElement> {
|
||||
/**
|
||||
* Label for the timeline row
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* Heading level for the label e.g h2, h3...
|
||||
* @default "h3"
|
||||
*/
|
||||
headingTag?: "h2" | "h3" | "h4" | "h5" | "h6";
|
||||
/**
|
||||
* Icon next to label
|
||||
*/
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface TimelineRowType
|
||||
extends React.ForwardRefExoticComponent<
|
||||
TimelineRowProps & React.RefAttributes<HTMLOListElement>
|
||||
> {
|
||||
componentType: TimelineComponentTypes;
|
||||
}
|
||||
|
||||
export const TimelineRow = forwardRef<HTMLOListElement, TimelineRowProps>(
|
||||
({ label, headingTag = "h3", icon, ...rest }, ref) => {
|
||||
const { periods, id, active } = useRowContext();
|
||||
const { setActiveRow } = useTimelineContext();
|
||||
|
||||
const latest = periods.reduce((a, b) => {
|
||||
return a.end > b.end ? a : b;
|
||||
}, {} as PositionedPeriod);
|
||||
|
||||
const earliest = periods.reduce((a, b) => {
|
||||
return a.end < b.end ? a : b;
|
||||
}, {} as PositionedPeriod);
|
||||
|
||||
const firstFocusable = periods.find(
|
||||
(p) => !!p.children || !!p.onSelectPeriod
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{label && (
|
||||
<BodyShort
|
||||
as={headingTag}
|
||||
id={`timeline-row-${id}`}
|
||||
className="navdsi-timeline__row-label"
|
||||
size="small"
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
</BodyShort>
|
||||
)}
|
||||
<div
|
||||
className={cl("navdsi-timeline__row", {
|
||||
"navdsi-timeline__row--active": active,
|
||||
})}
|
||||
>
|
||||
<ol
|
||||
{...rest}
|
||||
ref={ref}
|
||||
aria-label={
|
||||
periods.length === 0
|
||||
? "Ingen perioder"
|
||||
: `${format(earliest.start, "dd.MM.yyyy")} til ${format(
|
||||
latest.end,
|
||||
"dd.MM.yyyy"
|
||||
)}`
|
||||
}
|
||||
className="navdsi-timeline__row-periods"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
setActiveRow(e.key);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{periods &&
|
||||
periods.map((period) => {
|
||||
return (
|
||||
<li key={`period-${period.id}`}>
|
||||
<PeriodContext.Provider
|
||||
value={{
|
||||
periodId: period.id,
|
||||
firstFocus: firstFocusable?.id === period.id,
|
||||
restProps: period?.restProps,
|
||||
}}
|
||||
>
|
||||
<Period
|
||||
start={period.start}
|
||||
end={period.endInclusive}
|
||||
icon={period.icon}
|
||||
ref={period?.ref}
|
||||
/>
|
||||
</PeriodContext.Provider>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
) as TimelineRowType;
|
||||
|
||||
TimelineRow.componentType = "row";
|
||||
|
||||
export default TimelineRow;
|
||||
@@ -0,0 +1,22 @@
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
interface PeriodContextProps {
|
||||
periodId: String;
|
||||
firstFocus: boolean;
|
||||
restProps?: any;
|
||||
}
|
||||
|
||||
export const PeriodContext = createContext<PeriodContextProps>({
|
||||
periodId: "",
|
||||
firstFocus: false,
|
||||
});
|
||||
|
||||
export const usePeriodContext = () => {
|
||||
const context = useContext(PeriodContext);
|
||||
|
||||
if (!context) {
|
||||
console.warn("usePeriodContext must be used with PeriodContext");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import { PositionedPeriod } from "../utils/types.internal";
|
||||
|
||||
interface RowContextProps {
|
||||
periods: PositionedPeriod[];
|
||||
id: string;
|
||||
active: boolean;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export const RowContext = createContext<RowContextProps>({
|
||||
periods: [],
|
||||
id: "",
|
||||
active: false,
|
||||
index: 0,
|
||||
});
|
||||
|
||||
export const useRowContext = () => {
|
||||
const context = useContext(RowContext);
|
||||
|
||||
if (!context) {
|
||||
console.warn("useRowContext must be used with RowContext");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
interface TimelineContextProps {
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
direction: "left" | "right";
|
||||
setStart: (d: Date) => void;
|
||||
setEndInclusive: (d: Date) => void;
|
||||
activeRow: number | null;
|
||||
setActiveRow: (i: string) => void;
|
||||
initiate: (i: number) => void;
|
||||
addFocusable: (ref: HTMLButtonElement | null, id: number) => void;
|
||||
}
|
||||
|
||||
export const TimelineContext = createContext<TimelineContextProps>({
|
||||
startDate: new Date(),
|
||||
endDate: new Date(),
|
||||
direction: "left",
|
||||
setStart: () => null,
|
||||
setEndInclusive: () => null,
|
||||
activeRow: 0,
|
||||
setActiveRow: () => null,
|
||||
initiate: () => null,
|
||||
addFocusable: () => null,
|
||||
});
|
||||
|
||||
export const useTimelineContext = () => {
|
||||
const context = useContext(TimelineContext);
|
||||
|
||||
if (!context) {
|
||||
console.warn(
|
||||
"useTimelineContext must be used with TimelineContext (<Timeline />)"
|
||||
);
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { addDays, endOfDay, isAfter, startOfDay, subDays } from "date-fns";
|
||||
|
||||
import { lastPeriod } from "../utils/sort";
|
||||
import { Period } from "../utils/types.external";
|
||||
import {
|
||||
InternalSimpleTimeline,
|
||||
PositionedPeriod,
|
||||
} from "../utils/types.internal";
|
||||
|
||||
import { horizontalPositionAndWidth } from "../utils/calc";
|
||||
import { invisiblePeriods, withinADay } from "../utils/filter";
|
||||
|
||||
const spatialPeriod = (
|
||||
period: Period,
|
||||
timelineStart: Date,
|
||||
timelineEndInclusive: Date,
|
||||
direction: "left" | "right" = "left",
|
||||
i: number,
|
||||
periods: PositionedPeriod[],
|
||||
rowIndex: number
|
||||
): PositionedPeriod => {
|
||||
const start = period.start;
|
||||
const endInclusive = period.end;
|
||||
|
||||
const rightOverlap =
|
||||
i < periods.length - 1 && !isAfter(periods[i + 1].start, endInclusive);
|
||||
|
||||
const { horizontalPosition, width } = horizontalPositionAndWidth(
|
||||
startOfDay(start),
|
||||
endOfDay(
|
||||
rightOverlap ? startOfDay(subDays(periods[i + 1].start, 1)) : endInclusive
|
||||
),
|
||||
timelineStart,
|
||||
timelineEndInclusive
|
||||
);
|
||||
|
||||
return {
|
||||
id: `r-${rowIndex}-p-${i}`,
|
||||
start: start,
|
||||
endInclusive: endInclusive,
|
||||
horizontalPosition: horizontalPosition,
|
||||
direction: direction,
|
||||
width: width,
|
||||
end: endInclusive,
|
||||
status: period.status,
|
||||
onSelectPeriod: period.onSelectPeriod,
|
||||
icon: period.icon,
|
||||
children: period.children,
|
||||
isActive: period.isActive,
|
||||
statusLabel: period.statusLabel,
|
||||
};
|
||||
};
|
||||
|
||||
const adjustedEdges = (
|
||||
period: PositionedPeriod,
|
||||
i: number,
|
||||
allPeriods: PositionedPeriod[]
|
||||
): PositionedPeriod => {
|
||||
const left =
|
||||
i > 0 && withinADay(period.start, allPeriods[i - 1].endInclusive);
|
||||
const right =
|
||||
i < allPeriods.length - 1 &&
|
||||
withinADay(allPeriods[i + 1].start, period.endInclusive);
|
||||
|
||||
return left && right
|
||||
? { ...period, cropped: "both" }
|
||||
: left
|
||||
? { ...period, cropped: "left" }
|
||||
: right
|
||||
? { ...period, cropped: "right" }
|
||||
: period;
|
||||
};
|
||||
|
||||
const trimmedPeriods = (period: PositionedPeriod) => {
|
||||
let { horizontalPosition, width, cropped } = period;
|
||||
if (horizontalPosition + width > 100) {
|
||||
width = 100 - horizontalPosition;
|
||||
cropped = cropped === "left" || cropped === "both" ? "both" : "right";
|
||||
}
|
||||
if (horizontalPosition < 0 && horizontalPosition + width > 0) {
|
||||
width = horizontalPosition + width;
|
||||
horizontalPosition = 0;
|
||||
cropped = cropped === "right" || cropped === "both" ? "both" : "left";
|
||||
}
|
||||
|
||||
return {
|
||||
...period,
|
||||
width: width,
|
||||
horizontalPosition: horizontalPosition,
|
||||
cropped: cropped,
|
||||
};
|
||||
};
|
||||
|
||||
export const useTimelineRows = (
|
||||
rows: any,
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
direction: "left" | "right"
|
||||
): InternalSimpleTimeline[] =>
|
||||
useMemo(
|
||||
() =>
|
||||
rows.map((periods: InternalSimpleTimeline, i: number) => {
|
||||
const rowIndex = i;
|
||||
const timelinePeriods = periods.periods
|
||||
.sort((a: Period, b: Period) => a.start.valueOf() - b.start.valueOf())
|
||||
.map((period: Period & { restProps?: any; ref?: any }, i) => ({
|
||||
...spatialPeriod(
|
||||
period,
|
||||
startDate,
|
||||
endDate,
|
||||
direction,
|
||||
i,
|
||||
periods.periods,
|
||||
rowIndex
|
||||
),
|
||||
restProps: period?.restProps,
|
||||
ref: period?.ref,
|
||||
}))
|
||||
.sort(lastPeriod)
|
||||
.map(adjustedEdges)
|
||||
.map(trimmedPeriods)
|
||||
.filter(invisiblePeriods);
|
||||
return {
|
||||
id: `tl-row-${rowIndex}`,
|
||||
label: periods.label,
|
||||
headingTag: periods.headingTag || "h3",
|
||||
icon: periods.icon,
|
||||
periods:
|
||||
direction === "left" ? timelinePeriods : timelinePeriods.reverse(),
|
||||
restProps: periods?.restProps,
|
||||
ref: periods?.ref,
|
||||
};
|
||||
}),
|
||||
[rows, startDate, endDate, direction]
|
||||
);
|
||||
|
||||
const earliestDate = (earliest: Date, period: Period) =>
|
||||
period.start < earliest ? period.start : earliest;
|
||||
|
||||
const earliestFomDate = (rader: Period[][]) =>
|
||||
rader.flat().reduce(earliestDate, new Date());
|
||||
|
||||
export const useEarliestDate = ({ startDate, rows }: any) =>
|
||||
useMemo(
|
||||
() => (startDate ? startDate : earliestFomDate(rows)),
|
||||
[startDate, rows]
|
||||
);
|
||||
|
||||
const latestDate = (latest: Date, period: Period) =>
|
||||
period.end > latest ? period.end : latest;
|
||||
|
||||
const latestTomDate = (rows: Period[][]) =>
|
||||
rows.flat().reduce(latestDate, new Date(0));
|
||||
|
||||
export const useLatestDate = ({ endDate, rows }: any) =>
|
||||
useMemo(
|
||||
() => (endDate ? endDate : addDays(latestTomDate(rows), 1)),
|
||||
[endDate, rows]
|
||||
);
|
||||
@@ -0,0 +1,6 @@
|
||||
export { default as Timeline } from "./Timeline";
|
||||
export type { TimelineProps } from "./Timeline";
|
||||
export type { TimelineRowProps } from "./TimelineRow";
|
||||
export type { TimelinePinProps } from "./Pin";
|
||||
export type { TimelinePeriodProps } from "./period";
|
||||
export type { TimelineZoomButtonProps } from "./zoom/ZoomButton";
|
||||
@@ -0,0 +1,91 @@
|
||||
import { mergeRefs, Popover } from "@navikt/ds-react";
|
||||
import cl from "clsx";
|
||||
import React, { useMemo, useRef, useState } from "react";
|
||||
import { usePeriodContext } from "../hooks/usePeriodContext";
|
||||
import { useRowContext } from "../hooks/useRowContext";
|
||||
import { useTimelineContext } from "../hooks/useTimelineContext";
|
||||
import { ariaLabel, getConditionalClasses } from "../utils/period";
|
||||
import { PeriodProps } from "./index";
|
||||
|
||||
interface TimelineClickablePeriodProps extends PeriodProps {
|
||||
onSelectPeriod?: () => void;
|
||||
isActive?: boolean;
|
||||
periodRef: React.ForwardedRef<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
const ClickablePeriod = React.memo(
|
||||
({
|
||||
onSelectPeriod,
|
||||
start,
|
||||
end,
|
||||
status,
|
||||
cropped,
|
||||
direction,
|
||||
left,
|
||||
width,
|
||||
icon,
|
||||
children,
|
||||
isActive,
|
||||
statusLabel,
|
||||
restProps,
|
||||
periodRef,
|
||||
}: TimelineClickablePeriodProps) => {
|
||||
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
||||
const [selected, setSelected] = useState(false);
|
||||
const { index } = useRowContext();
|
||||
const { firstFocus } = usePeriodContext();
|
||||
const { initiate, addFocusable } = useTimelineContext();
|
||||
|
||||
const mergedRef = useMemo(
|
||||
() => mergeRefs([buttonRef, periodRef]),
|
||||
[periodRef]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
{...restProps}
|
||||
ref={(r) => {
|
||||
firstFocus && addFocusable(r, index);
|
||||
mergedRef(r);
|
||||
}}
|
||||
onClick={() => {
|
||||
children && setSelected((x) => !x);
|
||||
onSelectPeriod?.();
|
||||
}}
|
||||
aria-label={ariaLabel(start, end, status, statusLabel)}
|
||||
className={cl(
|
||||
"navdsi-timeline__period--clickable",
|
||||
getConditionalClasses(cropped, direction, status),
|
||||
restProps?.className,
|
||||
{
|
||||
"navdsi-timeline__period--selected": isActive,
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
width: `${width}%`,
|
||||
[direction]: `${left}%`,
|
||||
}}
|
||||
aria-expanded={children ? selected : undefined}
|
||||
onFocus={() => {
|
||||
initiate(index);
|
||||
}}
|
||||
>
|
||||
<div className="navdsi-timeline__period--inner">{icon}</div>
|
||||
</button>
|
||||
{children && (
|
||||
<Popover
|
||||
open={selected}
|
||||
onClose={() => setSelected(false)}
|
||||
anchorEl={buttonRef.current}
|
||||
strategy="fixed"
|
||||
>
|
||||
<Popover.Content>{children}</Popover.Content>
|
||||
</Popover>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default ClickablePeriod;
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
import { ariaLabel, getConditionalClasses } from "../utils/period";
|
||||
import { PeriodProps } from "./index";
|
||||
import cl from "clsx";
|
||||
|
||||
interface TimelineNonClickablePeriodProps extends PeriodProps {
|
||||
periodRef?: React.ForwardedRef<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const NonClickablePeriod = ({
|
||||
start,
|
||||
end,
|
||||
status,
|
||||
cropped,
|
||||
direction,
|
||||
left,
|
||||
width,
|
||||
icon,
|
||||
statusLabel,
|
||||
restProps,
|
||||
periodRef,
|
||||
}: TimelineNonClickablePeriodProps) => {
|
||||
return (
|
||||
<div
|
||||
ref={periodRef}
|
||||
{...restProps}
|
||||
className={cl(
|
||||
getConditionalClasses(cropped, direction, status),
|
||||
restProps?.classname
|
||||
)}
|
||||
style={{
|
||||
width: `${width}%`,
|
||||
[direction]: `${left}%`,
|
||||
}}
|
||||
>
|
||||
<div className="navdsi-timeline__period--inner">
|
||||
{icon}
|
||||
<p className="sr-only">{ariaLabel(start, end, status, statusLabel)}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NonClickablePeriod;
|
||||
@@ -0,0 +1,130 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { usePeriodContext } from "../hooks/usePeriodContext";
|
||||
import { useRowContext } from "../hooks/useRowContext";
|
||||
import { TimelineComponentTypes } from "../utils/types.internal";
|
||||
import ClickablePeriod from "./ClickablePeriod";
|
||||
import NonClickablePeriod from "./NonClickablePeriod";
|
||||
|
||||
export interface TimelinePeriodProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Period start date.
|
||||
*/
|
||||
start: Date;
|
||||
/**
|
||||
* Period end date.
|
||||
*/
|
||||
end: Date;
|
||||
/**
|
||||
* Icon for easier visual identification.
|
||||
*/
|
||||
icon?: React.ReactNode;
|
||||
/**
|
||||
* Period status.
|
||||
* @default "neutral"
|
||||
*/
|
||||
status?: "success" | "warning" | "danger" | "info" | "neutral";
|
||||
/**
|
||||
* Status label for screen-readers
|
||||
* e.g "Sykemeldt", "foreldrepermisjon"
|
||||
*/
|
||||
statusLabel?: string;
|
||||
/**
|
||||
* Callback when selecting a period.
|
||||
*/
|
||||
onSelectPeriod?: () => void;
|
||||
/**
|
||||
* Content displayed in Popover on click.
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Visual active inidcation on period
|
||||
* @note Make sure only one period is active at a time
|
||||
*/
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface PeriodProps {
|
||||
start: Date;
|
||||
end: Date;
|
||||
status: string;
|
||||
cropped: string;
|
||||
direction: string;
|
||||
width: Number;
|
||||
left: Number;
|
||||
icon?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
statusLabel?: string;
|
||||
restProps?: any;
|
||||
}
|
||||
|
||||
export interface PeriodType
|
||||
extends React.ForwardRefExoticComponent<
|
||||
TimelinePeriodProps &
|
||||
React.RefAttributes<HTMLDivElement | HTMLButtonElement>
|
||||
> {
|
||||
componentType: TimelineComponentTypes;
|
||||
}
|
||||
|
||||
export const Period = forwardRef<HTMLDivElement, TimelinePeriodProps>(
|
||||
({ icon }, ref) => {
|
||||
const { periods } = useRowContext();
|
||||
const { periodId, restProps } = usePeriodContext();
|
||||
|
||||
const period = periods.find((p) => p.id === periodId);
|
||||
|
||||
if (!period) {
|
||||
return <></>;
|
||||
}
|
||||
const {
|
||||
start,
|
||||
endInclusive,
|
||||
width,
|
||||
horizontalPosition,
|
||||
status = "neutral",
|
||||
onSelectPeriod,
|
||||
cropped,
|
||||
direction,
|
||||
children,
|
||||
isActive,
|
||||
statusLabel,
|
||||
} = period;
|
||||
|
||||
return onSelectPeriod || children ? (
|
||||
<ClickablePeriod
|
||||
periodRef={ref as React.ForwardedRef<HTMLButtonElement>}
|
||||
start={start}
|
||||
end={endInclusive}
|
||||
status={status}
|
||||
onSelectPeriod={onSelectPeriod}
|
||||
cropped={cropped || ""}
|
||||
direction={direction}
|
||||
width={width}
|
||||
left={horizontalPosition}
|
||||
icon={icon}
|
||||
children={children}
|
||||
isActive={isActive}
|
||||
statusLabel={statusLabel}
|
||||
restProps={restProps}
|
||||
/>
|
||||
) : (
|
||||
<NonClickablePeriod
|
||||
periodRef={ref}
|
||||
start={start}
|
||||
end={endInclusive}
|
||||
status={status}
|
||||
cropped={cropped || ""}
|
||||
direction={direction}
|
||||
width={width}
|
||||
left={horizontalPosition}
|
||||
icon={icon}
|
||||
statusLabel={statusLabel}
|
||||
restProps={restProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
) as PeriodType;
|
||||
|
||||
Period.componentType = "period";
|
||||
|
||||
export default Period;
|
||||
@@ -0,0 +1,326 @@
|
||||
import { SuccessFilled } from "@navikt/ds-icons";
|
||||
import { useState } from "@storybook/addons";
|
||||
import { Meta } from "@storybook/react";
|
||||
import * as React from "react";
|
||||
import Timeline from "./Timeline";
|
||||
|
||||
export default {
|
||||
title: "ds-react-internal/Timeline",
|
||||
component: Timeline,
|
||||
argTypes: {},
|
||||
} as Meta;
|
||||
|
||||
const DummyLabel = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: "239px",
|
||||
margin: 0,
|
||||
textAlign: "left",
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
<p>Periode: 15.07.2019 - 25.07.2019</p>
|
||||
<p>Utbetalt: 12 345,00 kr</p>
|
||||
<p style={{ color: "red" }}>Dager igjen: 0</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const row1 = [
|
||||
{
|
||||
id: "1",
|
||||
start: new Date("Jan 1 2022"),
|
||||
end: new Date("Jan 31 2022"),
|
||||
status: "success",
|
||||
icon: <SuccessFilled aria-hidden />,
|
||||
statusLabel: "Sykemeldt",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
start: new Date("Feb 1 2022"),
|
||||
end: new Date("Mar 15 2022"),
|
||||
status: "danger",
|
||||
onSelectPeriod: () => console.log("PERIOD SELECTED!"),
|
||||
statusLabel: "Sykemeldt",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
start: new Date("May 2 2022"),
|
||||
end: new Date("May 25 2022"),
|
||||
status: "success",
|
||||
icon: <SuccessFilled aria-hidden />,
|
||||
statusLabel: "Sykemeldt",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
start: new Date("Mar 1 2022"),
|
||||
end: new Date("Mar 31 2022"),
|
||||
status: "success",
|
||||
onSelectPeriod: () => console.log("PERIOD SELECTED!"),
|
||||
icon: <SuccessFilled aria-hidden />,
|
||||
label: "test",
|
||||
statusLabel: "Sykemeldt",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
start: new Date("Jul 1 2022"),
|
||||
end: new Date("Jul 31 2022"),
|
||||
status: "warning",
|
||||
icon: <SuccessFilled aria-hidden />,
|
||||
onSelectPeriod: () => console.log("PERIOD SELECTED!"),
|
||||
statusLabel: "Sykemeldt",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
start: new Date("Aug 1 2022"),
|
||||
end: new Date("Aug 30 2022"),
|
||||
status: "warning",
|
||||
icon: <SuccessFilled aria-hidden />,
|
||||
statusLabel: "Sykemeldt",
|
||||
},
|
||||
];
|
||||
|
||||
const row2 = [
|
||||
{
|
||||
id: "7",
|
||||
start: new Date("May 13 2022"),
|
||||
end: new Date("May 25 2022"),
|
||||
status: "warning",
|
||||
onSelectPeriod: () => console.log("PERIOD SELECTED!"),
|
||||
icon: <SuccessFilled aria-hidden />,
|
||||
children: <DummyLabel />,
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
start: new Date("Feb 1 2022"),
|
||||
end: new Date("May 2 2022"),
|
||||
status: "neutral",
|
||||
onSelectPeriod: () => console.log("PERIOD SELECTED!"),
|
||||
icon: <SuccessFilled aria-hidden />,
|
||||
children: <DummyLabel />,
|
||||
},
|
||||
];
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<div style={{ width: "80vw" }}>
|
||||
<Timeline>
|
||||
<Timeline.Row label="Row 1" icon={<SuccessFilled aria-hidden />}>
|
||||
{row1.map((p: any, i) => {
|
||||
return (
|
||||
<Timeline.Period
|
||||
key={p.id}
|
||||
start={p.start}
|
||||
end={p.end}
|
||||
status={p.status}
|
||||
icon={p.icon}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Timeline.Row>
|
||||
<Timeline.Row label="Row 2" icon={<SuccessFilled aria-hidden />}>
|
||||
{row2.map((p: any, i) => {
|
||||
return (
|
||||
<Timeline.Period
|
||||
key={p.id}
|
||||
start={p.start}
|
||||
end={p.end}
|
||||
status={p.status}
|
||||
icon={p.icon}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Timeline.Row>
|
||||
</Timeline>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithPins = () => {
|
||||
return (
|
||||
<div style={{ width: "80vw" }}>
|
||||
<Timeline>
|
||||
<Timeline.Pin date={new Date("Apr 15 2022")}>Pin 1</Timeline.Pin>
|
||||
<Timeline.Pin date={new Date("Jun 12 2022")}>Pin 2</Timeline.Pin>
|
||||
<Timeline.Pin date={new Date("Jul 28 2022")}>Pin 3</Timeline.Pin>
|
||||
<Timeline.Row label="Row 1" icon={<SuccessFilled aria-hidden />}>
|
||||
{row1.map((p: any, i) => {
|
||||
return (
|
||||
<Timeline.Period
|
||||
key={p.id}
|
||||
start={p.start}
|
||||
end={p.end}
|
||||
status={p.status}
|
||||
icon={p.icon}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Timeline.Row>
|
||||
<Timeline.Row label="Row 2" icon={<SuccessFilled aria-hidden />}>
|
||||
{row2.map((p: any, i) => {
|
||||
return (
|
||||
<Timeline.Period
|
||||
key={p.id}
|
||||
start={p.start}
|
||||
end={p.end}
|
||||
status={p.status}
|
||||
icon={p.icon}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Timeline.Row>
|
||||
</Timeline>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithZoom = () => {
|
||||
return (
|
||||
<div style={{ width: "80vw" }}>
|
||||
<Timeline>
|
||||
<Timeline.Row label="Row 1" icon={<SuccessFilled aria-hidden />}>
|
||||
{row1.map((p: any, i) => {
|
||||
return (
|
||||
<Timeline.Period
|
||||
key={p.id}
|
||||
start={p.start}
|
||||
end={p.end}
|
||||
status={p.status}
|
||||
icon={p.icon}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Timeline.Row>
|
||||
<Timeline.Row label="Row 2" icon={<SuccessFilled aria-hidden />}>
|
||||
{row2.map((p: any, i) => {
|
||||
return (
|
||||
<Timeline.Period
|
||||
key={p.id}
|
||||
start={p.start}
|
||||
end={p.end}
|
||||
status={p.status}
|
||||
icon={p.icon}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Timeline.Row>
|
||||
<Timeline.Zoom>
|
||||
<Timeline.Zoom.Button label="3 mnd" interval="month" count={3} />
|
||||
<Timeline.Zoom.Button label="7 mnd" interval="month" count={7} />
|
||||
<Timeline.Zoom.Button label="9 mnd" interval="month" count={9} />
|
||||
</Timeline.Zoom>
|
||||
</Timeline>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ActivePeriod = () => {
|
||||
const [activePeriod, setActivePeriod] = useState(row1[0]);
|
||||
|
||||
return (
|
||||
<div style={{ width: "80vw" }}>
|
||||
<Timeline>
|
||||
<Timeline.Pin date={new Date("Apr 15 2022")}>Pin 1</Timeline.Pin>
|
||||
<Timeline.Pin date={new Date("Jun 12 2022")}>Pin 2</Timeline.Pin>
|
||||
<Timeline.Pin date={new Date("Jul 28 2022")}>Pin 3</Timeline.Pin>
|
||||
<Timeline.Row label="Rad 1" icon={<SuccessFilled aria-hidden />}>
|
||||
{row1.map((p: any, i) => {
|
||||
return (
|
||||
<Timeline.Period
|
||||
key={p.id}
|
||||
start={p.start}
|
||||
end={p.end}
|
||||
status={p.status}
|
||||
onSelectPeriod={() => {
|
||||
setActivePeriod(p);
|
||||
p?.onSelectPeriod?.();
|
||||
}}
|
||||
icon={p.icon}
|
||||
isActive={activePeriod.id === p.id}
|
||||
statusLabel={p.statusLabel}
|
||||
>
|
||||
{p.children}
|
||||
</Timeline.Period>
|
||||
);
|
||||
})}
|
||||
</Timeline.Row>
|
||||
<Timeline.Row label="Rad 2">
|
||||
{row2.map((p: any, i) => {
|
||||
return (
|
||||
<Timeline.Period
|
||||
key={p.id}
|
||||
start={p.start}
|
||||
end={p.end}
|
||||
status={p.status}
|
||||
onSelectPeriod={() => {
|
||||
setActivePeriod(p);
|
||||
p?.onSelectPeriod?.();
|
||||
}}
|
||||
icon={p.icon}
|
||||
isActive={activePeriod.id === p.id}
|
||||
statusLabel={p.statusLabel}
|
||||
>
|
||||
{p.children}
|
||||
</Timeline.Period>
|
||||
);
|
||||
})}
|
||||
</Timeline.Row>
|
||||
<Timeline.Row label="Rad 3">
|
||||
<Timeline.Period
|
||||
start={new Date("Jan 1 2022")}
|
||||
end={new Date("Aug 20 2022")}
|
||||
status="info"
|
||||
icon={<SuccessFilled aria-hidden />}
|
||||
/>
|
||||
</Timeline.Row>
|
||||
</Timeline>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithDayLabels = () => {
|
||||
return (
|
||||
<div style={{ width: "80vw" }}>
|
||||
<Timeline>
|
||||
<Timeline.Row label="Row 1" icon={<SuccessFilled aria-hidden />}>
|
||||
<Timeline.Period
|
||||
start={new Date("Feb 4 2022")}
|
||||
end={new Date("Feb 13 2022")}
|
||||
status="success"
|
||||
/>
|
||||
</Timeline.Row>
|
||||
<Timeline.Row label="Row 2" icon={<SuccessFilled aria-hidden />}>
|
||||
<Timeline.Period
|
||||
start={new Date("Feb 17 2022")}
|
||||
end={new Date("Feb 22 2022")}
|
||||
status="warning"
|
||||
/>
|
||||
</Timeline.Row>
|
||||
</Timeline>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithYearLabels = () => {
|
||||
return (
|
||||
<div style={{ width: "80vw" }}>
|
||||
<Timeline>
|
||||
<Timeline.Row label="Row 1" icon={<SuccessFilled aria-hidden />}>
|
||||
<Timeline.Period
|
||||
start={new Date("Feb 4 2022")}
|
||||
end={new Date("Feb 13 2028")}
|
||||
status="success"
|
||||
/>
|
||||
</Timeline.Row>
|
||||
<Timeline.Row label="Row 2" icon={<SuccessFilled aria-hidden />}>
|
||||
<Timeline.Period
|
||||
start={new Date("Feb 17 2024")}
|
||||
end={new Date("Feb 22 2031")}
|
||||
status="warning"
|
||||
/>
|
||||
</Timeline.Row>
|
||||
</Timeline>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { differenceInMilliseconds } from "date-fns";
|
||||
|
||||
export const position = (date: Date, start: Date, endInclusive: Date) => {
|
||||
const diff = differenceInMilliseconds(endInclusive, start);
|
||||
return (differenceInMilliseconds(date, start) / diff) * 100;
|
||||
};
|
||||
|
||||
export const horizontalPositionAndWidth = (
|
||||
start: Date,
|
||||
endInclusive: Date,
|
||||
timelineStart: Date,
|
||||
timelineEndInclusive: Date
|
||||
) => {
|
||||
const horizontalPosition = position(
|
||||
start,
|
||||
timelineStart,
|
||||
timelineEndInclusive
|
||||
);
|
||||
const width =
|
||||
position(endInclusive, timelineStart, timelineEndInclusive) -
|
||||
horizontalPosition;
|
||||
return {
|
||||
horizontalPosition: horizontalPosition,
|
||||
width: width,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { differenceInDays } from "date-fns";
|
||||
import { PeriodProps } from "../period";
|
||||
import { Spatial } from "./types.internal";
|
||||
|
||||
export interface Positioned {
|
||||
horizontalPosition: number;
|
||||
direction: "left" | "right";
|
||||
}
|
||||
|
||||
export const isVisible = ({ horizontalPosition }: Positioned): boolean =>
|
||||
horizontalPosition <= 98 && horizontalPosition >= 0;
|
||||
|
||||
export const getFirstDate = (periods: any) => {
|
||||
return periods.sort(
|
||||
(a: PeriodProps, b: PeriodProps) => a.start.getTime() - b.start.getTime()
|
||||
)[0].start;
|
||||
};
|
||||
|
||||
export const getLastDate = (periods: any) => {
|
||||
return periods.sort(
|
||||
(a: PeriodProps, b: PeriodProps) => a.end.getTime() - b.end.getTime()
|
||||
)[periods.length - 1].end;
|
||||
};
|
||||
|
||||
export const withinADay = (date1: Date, date2: Date): boolean =>
|
||||
differenceInDays(date1, date2) <= 1;
|
||||
|
||||
export const invisiblePeriods = ({
|
||||
horizontalPosition,
|
||||
width,
|
||||
}: Positioned & Spatial) =>
|
||||
horizontalPosition >= 0 && horizontalPosition <= 100 && width > 0;
|
||||
@@ -0,0 +1 @@
|
||||
export { getFirstDate, getLastDate, isVisible, Positioned } from "./filter";
|
||||
@@ -0,0 +1,48 @@
|
||||
import cl from "clsx";
|
||||
import { format } from "date-fns";
|
||||
|
||||
export const getConditionalClasses = (
|
||||
cropped: string,
|
||||
direction: string,
|
||||
status: string
|
||||
) => {
|
||||
return cl(`navdsi-timeline__period navdsi-timeline__period--${status}`, {
|
||||
"navdsi-timeline__period--connected-both": cropped === "both",
|
||||
"navdsi-timeline__period--connected-right":
|
||||
(cropped === "right" && direction === "left") ||
|
||||
(cropped === "left" && direction === "right"),
|
||||
"navdsi-timeline__period--connected-left":
|
||||
(cropped === "left" && direction === "left") ||
|
||||
(cropped === "right" && direction === "right"),
|
||||
});
|
||||
};
|
||||
|
||||
const translateStatus = (status: string): string => {
|
||||
switch (status) {
|
||||
case "success":
|
||||
return "Suksess";
|
||||
case "warning":
|
||||
return "Advarsel";
|
||||
case "danger":
|
||||
return "Fare";
|
||||
case "info":
|
||||
return "Info";
|
||||
case "neutral":
|
||||
return "Nøytral";
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
};
|
||||
|
||||
export const ariaLabel = (
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
status: string,
|
||||
statusLabel?: string
|
||||
): string => {
|
||||
const start = format(startDate, "dd.MM.yyyy");
|
||||
const end = format(endDate, "dd.MM.yyyy");
|
||||
return `${
|
||||
statusLabel ? statusLabel : translateStatus(status)
|
||||
} fra ${start} til ${end}`;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { differenceInMilliseconds } from "date-fns";
|
||||
import { Period, PositionedPeriod } from "./types.internal";
|
||||
|
||||
export const lastDate = (a: Date, b: Date): number =>
|
||||
differenceInMilliseconds(a, b);
|
||||
|
||||
export const lastPeriod = (a: PositionedPeriod, b: PositionedPeriod): number =>
|
||||
a.horizontalPosition - b.horizontalPosition;
|
||||
|
||||
export const lastSinglePeriod = (a: Period, b: Period): number =>
|
||||
differenceInMilliseconds(b.endInclusive, a.endInclusive);
|
||||
@@ -0,0 +1,83 @@
|
||||
import { omit } from "@navikt/ds-react";
|
||||
import React, { ReactNode } from "react";
|
||||
import { Period } from "./types.external";
|
||||
|
||||
type ParsedChild = {
|
||||
label?: string;
|
||||
icon?: React.ReactNode;
|
||||
headingTag: string;
|
||||
periods: Omit<Period, "id" | "endInclusive">[];
|
||||
restProps: any;
|
||||
ref: any;
|
||||
};
|
||||
|
||||
export const parseRows = (rowChildren: ReactNode[]) => {
|
||||
let parsedChildren: ParsedChild[] = [];
|
||||
rowChildren?.forEach((r: React.ReactNode) => {
|
||||
let periods = [];
|
||||
if (React.isValidElement(r) && r?.props?.children) {
|
||||
if (Array.isArray(r.props.children)) {
|
||||
for (let i = 0; i < r.props.children.length; i++) {
|
||||
const p = r.props.children[i];
|
||||
|
||||
periods.push({
|
||||
start: p?.props?.start,
|
||||
end: p?.props?.end,
|
||||
status: p?.props?.status || "neutral",
|
||||
onSelectPeriod: p.props?.onSelectPeriod,
|
||||
label: r.props.label,
|
||||
icon: p.props.icon,
|
||||
children: p.props.children,
|
||||
isActive: p.props.isActive,
|
||||
statusLabel: p.props.statusLabel,
|
||||
restProps: omit(p.props, [
|
||||
"start",
|
||||
"end",
|
||||
"status",
|
||||
"onSelectPeriod",
|
||||
"label",
|
||||
"icon",
|
||||
"children",
|
||||
"isActive",
|
||||
"statusLabel",
|
||||
]),
|
||||
ref: p?.ref,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
periods.push({
|
||||
start: r.props.children.props.start,
|
||||
end: r.props.children.props.end,
|
||||
status: r.props.children.props?.status || "neutral",
|
||||
onSelectPeriod: r.props.children.props?.onSelectPeriod,
|
||||
label: r.props.label,
|
||||
icon: r.props.children.props?.icon,
|
||||
children: r.props.children.props?.children,
|
||||
statusLabel: r.props.children.props?.statusLabel,
|
||||
restProps: omit(r.props.children.props, [
|
||||
"start",
|
||||
"end",
|
||||
"status",
|
||||
"onSelectPeriod",
|
||||
"label",
|
||||
"icon",
|
||||
"children",
|
||||
"isActive",
|
||||
"statusLabel",
|
||||
]),
|
||||
ref: r.props?.children?.ref,
|
||||
});
|
||||
}
|
||||
parsedChildren.push({
|
||||
label: r.props.label,
|
||||
icon: r.props.icon,
|
||||
headingTag: r.props.headingTag,
|
||||
periods: periods,
|
||||
restProps: omit(r.props, ["label", "icon", "headingTag"]),
|
||||
ref: (r as any)?.ref,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return parsedChildren;
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
|
||||
export type PeriodStatus =
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger"
|
||||
| "info"
|
||||
| "neutral";
|
||||
|
||||
export type Percentage = number;
|
||||
|
||||
export interface Positioned {
|
||||
horizontalPosition: number;
|
||||
direction: "left" | "right";
|
||||
}
|
||||
|
||||
export interface Period {
|
||||
id: string;
|
||||
label?: string;
|
||||
start: Date;
|
||||
endInclusive: Date;
|
||||
status?: PeriodStatus;
|
||||
onSelectPeriod?: () => void;
|
||||
icon?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
end: Date;
|
||||
isActive?: boolean;
|
||||
statusLabel?: string;
|
||||
}
|
||||
|
||||
export interface PositionedPeriod extends Period, Positioned {
|
||||
width: number;
|
||||
active?: boolean;
|
||||
cropped?: "left" | "right" | "both";
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
hoverLabel?: React.ReactNode;
|
||||
infoPin?: boolean;
|
||||
start: Date;
|
||||
}
|
||||
|
||||
export interface Spatial {
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface AxisLabel extends Positioned, Spatial {
|
||||
label: string;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
export interface InternalSimpleTimeline {
|
||||
id: string;
|
||||
periods: PositionedPeriod[];
|
||||
}
|
||||
|
||||
export interface SimplePeriod {
|
||||
/**
|
||||
* Startdato for perioden, mao. periodens høyre kant.
|
||||
*/
|
||||
start: Date;
|
||||
/**
|
||||
* Sluttdato for perioden, mao. periodens venstre kant.
|
||||
*/
|
||||
end: Date;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from "react";
|
||||
|
||||
export type TimelineComponentTypes = "row" | "pin" | "period";
|
||||
|
||||
export type PeriodStatus =
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger"
|
||||
| "info"
|
||||
| "neutral";
|
||||
|
||||
export type Percentage = number;
|
||||
|
||||
export interface Positioned {
|
||||
horizontalPosition: number;
|
||||
direction: "left" | "right";
|
||||
}
|
||||
|
||||
export interface Period {
|
||||
id: string;
|
||||
start: Date;
|
||||
endInclusive: Date;
|
||||
status?: PeriodStatus;
|
||||
onSelectPeriod?: () => void;
|
||||
icon?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface PositionedPeriod extends Period, Positioned {
|
||||
width: number;
|
||||
active?: boolean;
|
||||
cropped?: "left" | "right" | "both";
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
hoverLabel?: React.ReactNode;
|
||||
infoPin?: boolean;
|
||||
end: Date;
|
||||
statusLabel?: string;
|
||||
restProps?: any;
|
||||
ref?: any;
|
||||
}
|
||||
|
||||
export interface Spatial {
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface AxisLabel extends Positioned, Spatial {
|
||||
label: string;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
export interface InternalSimpleTimeline {
|
||||
label: string;
|
||||
id: string;
|
||||
periods: PositionedPeriod[];
|
||||
icon?: React.ReactNode;
|
||||
headingTag: "h2" | "h3" | "h4" | "h5" | "h6";
|
||||
restProps: any;
|
||||
ref: any;
|
||||
}
|
||||
|
||||
export interface SelectedPeriod {
|
||||
/**
|
||||
* Start of the period.
|
||||
*/
|
||||
start: Date;
|
||||
/**
|
||||
* End of the period.
|
||||
*/
|
||||
end: Date;
|
||||
/**
|
||||
* Period id
|
||||
*/
|
||||
id: string;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { Detail } from "@navikt/ds-react";
|
||||
import {
|
||||
addMonths,
|
||||
addYears,
|
||||
format,
|
||||
isSameDay,
|
||||
subMonths,
|
||||
subYears,
|
||||
} from "date-fns";
|
||||
import React, { forwardRef } from "react";
|
||||
import { useTimelineContext } from "../hooks/useTimelineContext";
|
||||
|
||||
export interface TimelineZoomButtonProps {
|
||||
/**
|
||||
* e.g 3mnd, 6mnd etc
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* Zoom interval in months or years
|
||||
*/
|
||||
interval: "month" | "year";
|
||||
/**
|
||||
* How many units of the interval that will be applied
|
||||
* e.g interval="month" + count={3} equals 3 months
|
||||
*/
|
||||
count: number;
|
||||
}
|
||||
|
||||
export type ZoomButtonType = React.ForwardRefExoticComponent<
|
||||
TimelineZoomButtonProps & React.RefAttributes<HTMLButtonElement>
|
||||
>;
|
||||
|
||||
export const ZoomButton: ZoomButtonType = forwardRef(
|
||||
({ label, interval, count, ...rest }, ref) => {
|
||||
const { setStart, endDate, startDate, direction } = useTimelineContext();
|
||||
|
||||
let startOfRange: Date;
|
||||
|
||||
if (interval === "month") {
|
||||
startOfRange =
|
||||
direction === "left"
|
||||
? subMonths(endDate, count)
|
||||
: addMonths(startDate, count);
|
||||
} else {
|
||||
startOfRange =
|
||||
direction === "left"
|
||||
? subYears(endDate, count)
|
||||
: addYears(startDate, count);
|
||||
}
|
||||
|
||||
const currentZoom =
|
||||
direction === "left"
|
||||
? isSameDay(startDate, startOfRange)
|
||||
: isSameDay(endDate, startOfRange);
|
||||
|
||||
return (
|
||||
<Detail as="li">
|
||||
<button
|
||||
aria-label={
|
||||
!currentZoom
|
||||
? `Zoom tidslinjen ${format(
|
||||
startOfRange,
|
||||
"dd.MM.yyyy"
|
||||
)} til ${format(endDate, "dd.MM.yyyy")}`
|
||||
: "Tilbakestill til initiell tidsperspektiv"
|
||||
}
|
||||
ref={ref}
|
||||
{...rest}
|
||||
className="navdsi-timeline__zoom-button"
|
||||
onClick={() => {
|
||||
setStart(startOfRange);
|
||||
}}
|
||||
aria-pressed={currentZoom}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
</Detail>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default ZoomButton;
|
||||
@@ -0,0 +1,34 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import ZoomButton, { ZoomButtonType } from "./ZoomButton";
|
||||
|
||||
interface ZoomProps extends React.HTMLAttributes<HTMLUListElement> {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface ZoomType<Props = ZoomProps>
|
||||
extends React.ForwardRefExoticComponent<
|
||||
Props & React.RefAttributes<HTMLUListElement>
|
||||
> {
|
||||
Button: ZoomButtonType;
|
||||
componentType: string;
|
||||
}
|
||||
|
||||
export const Zoom = forwardRef<HTMLUListElement, ZoomProps>(
|
||||
({ className, children, ...rest }, ref) => {
|
||||
return (
|
||||
<ul
|
||||
ref={ref}
|
||||
className={cl(className, "navdsi-timeline__zoom")}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
) as ZoomType;
|
||||
|
||||
Zoom.Button = ZoomButton;
|
||||
Zoom.componentType = "zoom";
|
||||
|
||||
export default Zoom;
|
||||
@@ -1,22 +1,17 @@
|
||||
CMS og nettside for [aksel.nav.no](https://aksel.nav.no/)
|
||||
|
||||
```sh
|
||||
├── /website # NEXT.JS app (v12)
|
||||
└── /sanity # Sanity CMS-app (v2)
|
||||
├── /website # NEXT.JS app (v12)
|
||||
└── /website/sanity # Sanity CMS-app (v3)
|
||||
```
|
||||
|
||||
### /website
|
||||
|
||||
[Nextjs](https://nextjs.org/) applikasjon som henter data fra et [sanity-datasett](https://www.sanity.io/).
|
||||
|
||||
Siden leverer to løsninger:
|
||||
### /website/sanity
|
||||
|
||||
- Innhold på toppnivå i Aksel: artikler, prinsipper og blogger
|
||||
- Designsystem-dokumentasjon under https://aksel.nav.no/designsystem
|
||||
|
||||
### /sanity
|
||||
|
||||
Det publiserte studioet finner man på [aksel.nav.no/studio](https://aksel.nav.no/studio)(krever NAV SSO eller invite).
|
||||
Det publiserte studioet finner man på [aksel.nav.no/admin](https://aksel.nav.no/admin)(krever NAV SSO eller invite).
|
||||
|
||||
`production`-datasettet er privat, så man må ha riktige tilganger for å kunne lese/jobbe med dataen der.
|
||||
|
||||
@@ -42,45 +37,15 @@ SANITY_PREVIEW_TOKEN
|
||||
SANITY_PRIVATE_NO_DRAFTS
|
||||
```
|
||||
|
||||
### Sanity (localhost:3333)
|
||||
|
||||
> !! Må være developer i https://verktoykasse.sanity.studio, Ta kontakt med team Aksel om du trenger tilgang !!
|
||||
|
||||
Husk at alle endringer i datasettet lokalt også gjøres i prod. Bytt derfor til `development`-dataset hvis man vil teste destruktive handlinger. Dette kan endres i `/sanity/schema.json`
|
||||
|
||||
```
|
||||
yarn install
|
||||
yarn workspace aksel.nav.no sanity
|
||||
```
|
||||
|
||||
**Deploy av ny sanity-oppdatering**
|
||||
|
||||
```
|
||||
yarn workspace aksel.nav.no sanity:deploy
|
||||
```
|
||||
|
||||
### .env
|
||||
|
||||
Blir brukt flere keys i .env under `./website`, men kun `SANITY_PRIVATE_NO_DRAFTS` trengs for å teste lokalt
|
||||
|
||||
- SANITY_WRITE_KEY:
|
||||
For å sende dokumenter til sanity, brukt til å oppdatere sandboxes/examples/farger. Trengs bare hvis kode skal synkes fra lokal branch
|
||||
- ALGOLIA_ADMIN:
|
||||
For å oppdatere records i søkemotoren. Trengs bare hvis man oppdaterer søkeindexen lokalt.
|
||||
- ALGOLIA_ID:
|
||||
Søkemotor index-id for å redigere riktig Index. Trengs bare for at søk skal fungere lokalt
|
||||
- TRELLO_TOKEN: Sender feedback til et egent trello-board. Ikke nødvendig lokalt hvis man ikke vil teste feedback-modulen.
|
||||
- SANITY_PREVIEW_TOKEN: Gir appen tilgang til å lese draft innhold fra sanity i "preview"-mode. Trengs for å teste preview-sider
|
||||
For å sende dokumenter til sanity, brukt til å oppdatere sandboxes/examples/farger. Trengs bare hvis kode skal synkes fra lokal branch/teste feedback-løsning
|
||||
- SANITY_PREVIEW_TOKEN: Gir appen tilgang til å lese draft innhold fra sanity i "preview"-mode. Trengs for å teste ikke publiserte-sider
|
||||
- SANITY_PRIVATE_NO_DRAFTS: Gir appen tilgang til å lese innhold fra Sanity da datasettet er privat
|
||||
|
||||
### Søk
|
||||
|
||||
Søk-index blir generert av [algolia-scraper](https://github.com/algolia/docsearch-scraper). Kjøres daglig i en github-action, men kan kjøres lokalt også med docker.
|
||||
|
||||
```
|
||||
docker run -it --env-file=.env -e "CONFIG=$(cat crawler.json | jq -r tostring)" algolia/docsearch-scraper
|
||||
```
|
||||
|
||||
## Backups
|
||||
|
||||
Kjøres backups hver 3 dag til GCP-bucket under `designsystem-prod`
|
||||
@@ -89,4 +54,4 @@ Kjøres backups hver 3 dag til GCP-bucket under `designsystem-prod`
|
||||
|
||||
Spørsmål og kontakt kan rettes til team Aksel
|
||||
|
||||
Aksel har også en egen slack-kanal #aksel
|
||||
Aksel har også en egen Slack-kanal #Aksel-designsystemet
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"index_name": "aksel_docsearch",
|
||||
"start_urls": [
|
||||
"https://aksel.nav.no/",
|
||||
{
|
||||
"url": "https://aksel.nav.no/designsystem",
|
||||
"tags": ["designsystem", "docs"]
|
||||
},
|
||||
{
|
||||
"url": "https://aksel.nav.no/god-praksis/",
|
||||
"tags": ["aksel"],
|
||||
"selectors_key": "aksel"
|
||||
},
|
||||
{
|
||||
"url": "https://aksel.nav.no/produktbloggen/",
|
||||
"tags": ["aksel"],
|
||||
"selectors_key": "aksel"
|
||||
},
|
||||
{
|
||||
"url": "https://aksel.nav.no/prinsipper/",
|
||||
"tags": ["aksel"],
|
||||
"selectors_key": "aksel"
|
||||
}
|
||||
],
|
||||
"selectors": {
|
||||
"default": {
|
||||
"lvl0": {
|
||||
"selector": "h1.algolia-index-lvl1",
|
||||
"global": true
|
||||
},
|
||||
"lvl1": ".algolia-index-tabs--active",
|
||||
"lvl2": "h2.algolia-index-lvl2",
|
||||
"lvl3": "h3.algolia-index-lvl3",
|
||||
"text": "p.algolia-index-body, p.algolia-index-ingress"
|
||||
},
|
||||
"aksel": {
|
||||
"lvl0": {
|
||||
"selector": "h1.algolia-index-lvl1",
|
||||
"global": true
|
||||
},
|
||||
"lvl1": "h2.algolia-index-lvl2",
|
||||
"lvl2": "h3.algolia-index-lvl3",
|
||||
"text": "p.algolia-index-body, p.algolia-index-ingress"
|
||||
}
|
||||
},
|
||||
"selectors_exclude": [".algolia-ignore-index"],
|
||||
"stop_urls": [
|
||||
"https://aksel.nav.no/preview",
|
||||
"https://old-design-nav.vercel.app",
|
||||
"https://aksel.nav.no/eksempler"
|
||||
],
|
||||
"sitemap_urls": ["https://aksel.nav.no/api/sitemap"],
|
||||
"js_render": true
|
||||
}
|
||||
@@ -15,14 +15,14 @@
|
||||
"website"
|
||||
],
|
||||
"dependencies": {
|
||||
"@navikt/ds-codemod": "^2.0.11",
|
||||
"@navikt/ds-css": "^2.0.11",
|
||||
"@navikt/ds-css-internal": "^2.0.11",
|
||||
"@navikt/ds-icons": "^2.0.11",
|
||||
"@navikt/ds-react": "^2.0.11",
|
||||
"@navikt/ds-react-internal": "^2.0.11",
|
||||
"@navikt/ds-tailwind": "^2.0.11",
|
||||
"@navikt/ds-tokens": "^2.0.11"
|
||||
"@navikt/ds-codemod": "^2.1.1",
|
||||
"@navikt/ds-css": "^2.1.1",
|
||||
"@navikt/ds-css-internal": "^2.1.1",
|
||||
"@navikt/ds-icons": "^2.1.1",
|
||||
"@navikt/ds-react": "^2.1.1",
|
||||
"@navikt/ds-react-internal": "^2.1.1",
|
||||
"@navikt/ds-tailwind": "^2.1.1",
|
||||
"@navikt/ds-tokens": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier-plugin-tailwindcss": "^0.1.13"
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
.yarn/cache
|
||||
production.tar.gz
|
||||
*.tar.gz
|
||||
@@ -62,8 +62,8 @@ export default function SetPrivateAction(props) {
|
||||
dele offentlig kan medføre eks omdømmetap og uønsket oppmerksomhet
|
||||
utenfra. Dobbelsjekk med temaansvarlig og andre som har skrevet
|
||||
artikkelen før du velger å åpne innholdet for alle. Er du usikker
|
||||
er det bare å sende en melding på #aksel eller #aksel-redaktører
|
||||
på slack.
|
||||
er det bare å sende en melding på #Aksel-designsystemet eller
|
||||
#aksel-redaktører på Slack.
|
||||
</BodyLong>
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -1,57 +1,53 @@
|
||||
import { useEffect } from "react";
|
||||
import config from "config:sanity";
|
||||
|
||||
const BUNDLE_CHECK_INTERVAL = 60 * 5000;
|
||||
const BUNDLE_CHECK_INTERVAL_DECLINED = 60 * 20000;
|
||||
const CHANGES_AVAILABLE_MESSAGE =
|
||||
"Studioet er har nye endringer! For å ta i bruk disse vil siden nå oppdateres for den nyeste versjonen.";
|
||||
|
||||
async function getCurrentHash() {
|
||||
const basePath = (config.project && config.project.basePath) || "/";
|
||||
const html = await window.fetch(basePath).then((res) => res.text());
|
||||
const [, hash] = html.match(/app\.bundle\.js\?(\w+)/) || [];
|
||||
return hash;
|
||||
}
|
||||
|
||||
let hash = null;
|
||||
let interval = null;
|
||||
import { Send } from "@navikt/ds-icons";
|
||||
import { BodyLong, Button, Heading, Modal } from "@navikt/ds-react";
|
||||
import React, { useRef, useState } from "react";
|
||||
|
||||
const BundleChecker = () => {
|
||||
useEffect(() => {
|
||||
getCurrentHash().then((newHash) => {
|
||||
hash = newHash;
|
||||
});
|
||||
const [inc, setInc] = useState(0);
|
||||
const interval = useRef(null);
|
||||
|
||||
interval = createInterval();
|
||||
const start = () => {
|
||||
interval.current = setInterval(() => {
|
||||
setInc((x) => x + 0.4);
|
||||
}, 10);
|
||||
};
|
||||
if (inc > 99) {
|
||||
clearInterval(interval.current);
|
||||
window.location.replace("https://aksel.nav.no/admin");
|
||||
}
|
||||
|
||||
return () => clearInterval(interval);
|
||||
document.body.style.opacity = `${100 - inc}%`;
|
||||
document.body.style.scale = `${1 - inc / 150}`;
|
||||
document.body.style.rotate = `${inc ** 1.3}deg`;
|
||||
|
||||
/*
|
||||
TODO: @release uncomment for redirecting to new studio
|
||||
*/
|
||||
/* window.location.replace(`http://aksel.nav.no/admin`); */
|
||||
}, []);
|
||||
return (
|
||||
<Modal
|
||||
aria-labelledby="modal-heading"
|
||||
closeButton={false}
|
||||
open
|
||||
onClose={() => null}
|
||||
>
|
||||
<Modal.Content style={{ minWidth: "20rem", maxWidth: "40rem" }}>
|
||||
<Heading spacing id="modal-heading" size="medium" level="1">
|
||||
Studioet er flyttet!
|
||||
</Heading>
|
||||
|
||||
// We're a react component, in theory, so return null to not render anything
|
||||
return null;
|
||||
<BodyLong spacing>
|
||||
Vi har oppdatert studioet og flyttet det til ny url:
|
||||
aksel.nav.no/admin. Hvis noe ikke fungerer, ta kontakt på slack
|
||||
#aksel-redaksjonen! Husk å logge inn med samme login-provider. For de
|
||||
fleste vil dette være NAV SSO.
|
||||
</BodyLong>
|
||||
<Button
|
||||
onClick={() => start()}
|
||||
icon={<Send aria-hidden />}
|
||||
iconPosition="right"
|
||||
>
|
||||
Gå til nytt studio
|
||||
</Button>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default BundleChecker;
|
||||
|
||||
const createInterval = (declined?: boolean) =>
|
||||
setInterval(
|
||||
async () => {
|
||||
const newHash = await getCurrentHash();
|
||||
|
||||
if (hash && newHash !== hash) {
|
||||
clearInterval(interval);
|
||||
|
||||
if (window.confirm(CHANGES_AVAILABLE_MESSAGE)) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
interval = createInterval(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
declined ? BUNDLE_CHECK_INTERVAL_DECLINED : BUNDLE_CHECK_INTERVAL
|
||||
);
|
||||
@@ -75,8 +75,8 @@ const IntroPage = React.forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||
>
|
||||
<ReadMore header="Har du ikke de rollene du trenger?">
|
||||
Send en melding til oss på{" "}
|
||||
<Link href="https://nav-it.slack.com/archives/C0370ADS0HX">
|
||||
#Aksel
|
||||
<Link href="https://nav-it.slack.com/archives/C7NE7A8UF">
|
||||
#Aksel-designsystemet
|
||||
</Link>{" "}
|
||||
slack så fikser vi det.
|
||||
</ReadMore>
|
||||
|
||||
@@ -119,7 +119,6 @@ const adjektiv = [
|
||||
const client = sanityClient.withConfig({ apiVersion: "2020-06-19" });
|
||||
|
||||
const generateName = async () => {
|
||||
console.log("ran");
|
||||
const names = await client.fetch(`*[_type == "editor"].anon_navn.current`);
|
||||
|
||||
let c = 0;
|
||||
@@ -131,7 +130,6 @@ const generateName = async () => {
|
||||
c += 1;
|
||||
}
|
||||
|
||||
console.log(res);
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const exportDataset = require("@sanity/export");
|
||||
const sanityClient = require("@sanity/client");
|
||||
const sanityToken = process.env.SANITY_TOKEN;
|
||||
const sanityToken = process.env.SANITY_PREVIEW_TOKEN;
|
||||
const SanityConfig = require("../sanity.json");
|
||||
|
||||
if (!sanityToken) {
|
||||
throw new Error("Could not find token from SANITY_TOKEN");
|
||||
throw new Error("Could not find token from SANITY_PREVIEW_TOKEN");
|
||||
}
|
||||
|
||||
const client = sanityClient({
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
const AkselLogo = (props): JSX.Element => (
|
||||
<svg
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 28 28"
|
||||
width="28"
|
||||
height="28"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="m3 9 5.268-5.268A2.5 2.5 0 0 1 10.036 3H22.5A2.5 2.5 0 0 1 25 5.5v12.465a2.5 2.5 0 0 1-.732 1.767L19 25"
|
||||
></path>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="m3 9 5.232-5.232a2.5 2.5 0 0 1 3.536 0l12.464 12.464a2.5 2.5 0 0 1 0 3.536L19 25m-7-9-9 9"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default AkselLogo;
|
||||
@@ -1,116 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const FigmaIcon = ({ ...props }): JSX.Element => (
|
||||
<svg
|
||||
{...props}
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 18 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
focusable="false"
|
||||
aria-label="Figma"
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
d="M15.5 4.99999C15.5 6.93299 13.9531 8.49999 12.0449 8.49999H8.5V1.5H12.0449C13.9531 1.5 15.5 3.067 15.5 4.99999Z"
|
||||
fill="#FF7262"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
d="M1.5 4.99999C1.5 6.93299 3.04691 8.49999 4.95513 8.49999H8.5V1.5H4.95513C3.04691 1.5 1.5 3.067 1.5 4.99999Z"
|
||||
fill="#F24E1E"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
d="M1.5 12C1.5 13.933 3.04691 15.5 4.95513 15.5H8.5V8.5H4.95513C3.04691 8.5 1.5 10.067 1.5 12Z"
|
||||
fill="#A259FF"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
d="M1.5 19C1.5 20.933 3.0692 22.5 4.97742 22.5C6.91042 22.5 8.5 20.9126 8.5 18.9545V15.5H4.95513C3.04691 15.5 1.5 17.067 1.5 19Z"
|
||||
fill="#0ACF83"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
d="M8.5 12C8.5 13.933 10.0469 15.5 11.9551 15.5H12.0449C13.9531 15.5 15.5 13.933 15.5 12C15.5 10.067 13.9531 8.5 12.0449 8.5H11.9551C10.0469 8.5 8.5 10.067 8.5 12Z"
|
||||
fill="#1ABCFE"
|
||||
stroke="#262626"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const FigmaIconNoSync = ({ ...props }): JSX.Element => (
|
||||
<svg
|
||||
{...props}
|
||||
width="1.5rem"
|
||||
height="1.5rem"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
focusable="false"
|
||||
aria-label="Figma ikke i sync"
|
||||
>
|
||||
<path d="M1.5 22.5L22.5 1.5" stroke="#BA3A26" strokeWidth="2" />
|
||||
<path
|
||||
d="M18.5 4.99999C18.5 6.93299 16.9531 8.49999 15.0449 8.49999H11.5V1.5H15.0449C16.9531 1.5 18.5 3.067 18.5 4.99999Z"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
d="M4.5 4.99999C4.5 6.93299 6.04691 8.49999 7.95513 8.49999H11.5V1.5H7.95513C6.04691 1.5 4.5 3.067 4.5 4.99999Z"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
d="M4.5 12C4.5 13.933 6.04691 15.5 7.95513 15.5H11.5V8.5H7.95513C6.04691 8.5 4.5 10.067 4.5 12Z"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
d="M4.5 19C4.5 20.933 6.0692 22.5 7.97742 22.5C9.91042 22.5 11.5 20.9126 11.5 18.9545V15.5H7.95513C6.04691 15.5 4.5 17.067 4.5 19Z"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
d="M11.5 12C11.5 13.933 13.0469 15.5 14.9551 15.5H15.0449C16.9531 15.5 18.5 13.933 18.5 12C18.5 10.067 16.9531 8.5 15.0449 8.5H14.9551C13.0469 8.5 11.5 10.067 11.5 12Z"
|
||||
stroke="#262626"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M15 17.335H16.6664C15.8573 17.9444 15.3333 18.9147 15.3333 20.0081C15.3333 21.319 16.0865 22.453 17.1812 23.0001L17.4786 22.402C16.6014 21.9636 16 21.056 16 20.0081C16 19.0192 16.5357 18.1551 17.3333 17.6925V19.674H18V16.6667H15V17.335ZM22.6667 19.9921C22.6667 21.0855 22.1427 22.0558 21.3336 22.6651H23V23.3334L20 23.3334V20.3262H20.6667V22.3076C21.4643 21.8451 22 20.981 22 19.9921C22 18.9442 21.3986 18.0366 20.5214 17.5982L20.8188 17.0001C21.9135 17.5472 22.6667 18.6811 22.6667 19.9921Z"
|
||||
fill="#262626"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const FigmaIconGrayScale = ({ ...props }): JSX.Element => (
|
||||
<svg
|
||||
width="18"
|
||||
height="26"
|
||||
viewBox="0 0 18 26"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
aria-label="Figma ikon"
|
||||
focusable="false"
|
||||
>
|
||||
<g clipPath="url(#clip0)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M0.459473 5.44999C0.459473 2.97008 2.46984 0.959717 4.94975 0.959717H8.06672H9.53756H12.6545C15.1344 0.959717 17.1448 2.97008 17.1448 5.44999C17.1448 7.02029 16.3387 8.40232 15.1178 9.2049C16.3387 10.0075 17.1448 11.3895 17.1448 12.9598C17.1448 15.4397 15.1344 17.4501 12.6545 17.4501H12.557C11.3944 17.4501 10.335 17.0082 9.53756 16.2833V17.4501V20.4207C9.53756 22.9336 7.47466 24.9597 4.97398 24.9597C2.50023 24.9597 0.459473 22.9555 0.459473 20.4694C0.459473 18.8992 1.26547 17.5172 2.48632 16.7146C1.26547 15.912 0.459473 14.53 0.459473 12.9598C0.459473 11.3895 1.26553 10.0075 2.48647 9.2049C1.26553 8.40232 0.459473 7.02029 0.459473 5.44999ZM8.06672 12.9598V9.94037H4.94975C3.28215 9.94037 1.93031 11.2922 1.93031 12.9598C1.93031 14.6214 3.2725 15.9696 4.93186 15.9792L4.94975 15.9792H8.06672V12.9598ZM9.53756 12.9598C9.53756 14.6274 10.8894 15.9792 12.557 15.9792H12.6545C14.3221 15.9792 15.674 14.6274 15.674 12.9598C15.674 11.2922 14.3221 9.94037 12.6545 9.94037H12.557C10.8894 9.94037 9.53756 11.2922 9.53756 12.9598ZM4.94975 17.4501L4.93195 17.45C3.27255 17.4596 1.93031 18.8078 1.93031 20.4694C1.93031 22.1309 3.30021 23.4889 4.97398 23.4889C6.67467 23.4889 8.06672 22.1091 8.06672 20.4207V17.4501H4.94975ZM4.94975 2.43055H8.06672V8.46943H4.94975C3.28215 8.46943 1.93031 7.11758 1.93031 5.44999C1.93031 3.7824 3.28215 2.43055 4.94975 2.43055ZM9.53756 8.46943V2.43055H12.6545C14.3221 2.43055 15.674 3.7824 15.674 5.44999C15.674 7.11758 14.3221 8.46943 12.6545 8.46943H9.53756Z"
|
||||
fill="var(--a-text-subtle)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect
|
||||
width="18"
|
||||
height="25"
|
||||
fill="white"
|
||||
transform="translate(0 0.5)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
@@ -0,0 +1,73 @@
|
||||
export const ChangelogIcon = () => (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
focusable={false}
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10.4688 2.26716C10.3077 1.83185 9.69206 1.83185 9.53098 2.26716L9.26942 2.97401C9.21878 3.11087 9.11087 3.21877 8.97401 3.26942L8.26716 3.53098C7.83185 3.69206 7.83185 4.30775 8.26716 4.46883L8.97401 4.73039C9.11087 4.78103 9.21878 4.88894 9.26942 5.0258L9.53098 5.73265C9.69206 6.16796 10.3077 6.16796 10.4688 5.73265L10.7304 5.0258C10.781 4.88894 10.8889 4.78103 11.0258 4.73039L11.7327 4.46883C12.168 4.30775 12.168 3.69206 11.7327 3.53098L11.0258 3.26942C10.8889 3.21877 10.781 3.11087 10.7304 2.97401L10.4688 2.26716ZM4.53098 5.26715C4.69206 4.83185 5.30775 4.83185 5.46883 5.26716L6.00048 6.70392C6.05112 6.84078 6.15903 6.94868 6.29589 6.99933L7.73265 7.53098C8.16796 7.69206 8.16796 8.30775 7.73265 8.46883L6.29589 9.00048C6.15903 9.05112 6.05112 9.15903 6.00048 9.29589L5.46883 10.7327C5.30775 11.168 4.69206 11.168 4.53098 10.7326L3.99933 9.29589C3.94868 9.15903 3.84078 9.05112 3.70392 9.00048L2.26716 8.46883C1.83185 8.30775 1.83185 7.69206 2.26716 7.53098L3.70392 6.99933C3.84078 6.94868 3.94868 6.84078 3.99933 6.70392L4.53098 5.26715ZM12.531 8.26716C12.6921 7.83185 13.3077 7.83185 13.4688 8.26716L14.5407 11.1637C14.5913 11.3006 14.6992 11.4085 14.8361 11.4591L17.7326 12.531C18.168 12.6921 18.168 13.3077 17.7326 13.4688L14.8361 14.5407C14.6992 14.5913 14.5913 14.6992 14.5407 14.8361L13.4688 17.7326C13.3078 18.168 12.6921 18.168 12.531 17.7326L11.4591 14.8361C11.4085 14.6992 11.3006 14.5913 11.1637 14.5407L8.26716 13.4688C7.83185 13.3077 7.83185 12.6921 8.26716 12.531L11.1637 11.4591C11.3006 11.4085 11.4085 11.3006 11.4591 11.1637L12.531 8.26716Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const FigmaIcon = () => (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
focusable={false}
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M3 4C3 1.80306 4.76151 0 6.96154 0H9H10H11H13.0385C15.2385 0 17 1.80306 17 4C17 5.19041 16.4828 6.26518 15.6589 7.00012C16.4828 7.73506 17 8.80982 17 10.0002C17 12.1972 15.2385 14.0002 13.0385 14.0002H12.9615C12.2462 14.0002 11.5772 13.8096 11 13.4768V14.0002V15.9608C11 18.1876 9.19358 19.9998 6.98065 19.9998C4.78895 19.9998 3 18.2051 3 15.9998C3 14.8095 3.51701 13.7349 4.34067 13C3.51701 12.2651 3 11.1905 3 10.0002C3 8.80983 3.51718 7.73506 4.34108 7.00012C3.51718 6.26518 3 5.19041 3 4ZM9 10.0002V8.00024H6.96154C5.89033 8.00024 5 8.88346 5 10.0002C5 11.1046 5.87059 11.9805 6.92578 11.9999L6.96154 11.9998H9V10.0002ZM11 10.0002C11 11.117 11.8903 12.0002 12.9615 12.0002H13.0385C14.1097 12.0002 15 11.117 15 10.0002C15 8.88346 14.1097 8.00024 13.0385 8.00024H12.9615C11.8903 8.00024 11 8.88346 11 10.0002ZM6.96154 14.0002L6.92578 14.0001C5.87059 14.0195 5 14.8954 5 15.9998C5 17.1081 5.90111 17.9998 6.98065 17.9998C8.08142 17.9998 9 17.0907 9 15.9608V14.0002H6.96154ZM6.96154 2H9V6H6.96154C5.89033 6 5 5.11678 5 4C5 2.88323 5.89033 2 6.96154 2ZM11 6V2H13.0385C14.1097 2 15 2.88323 15 4C15 5.11678 14.1097 6 13.0385 6H11Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const GithubIcon = () => (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
focusable={false}
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9.9702 0C4.45694 0 0 4.4898 0 10.0443C0 14.4843 2.85571 18.2427 6.81735 19.5729C7.31265 19.6729 7.49408 19.3567 7.49408 19.0908C7.49408 18.858 7.47775 18.0598 7.47775 17.2282C4.70429 17.8269 4.12673 16.0308 4.12673 16.0308C3.68102 14.8667 3.02061 14.5675 3.02061 14.5675C2.11286 13.9522 3.08673 13.9522 3.08673 13.9522C4.09367 14.0188 4.62204 14.9833 4.62204 14.9833C5.51327 16.5131 6.94939 16.0808 7.52714 15.8147C7.60959 15.1661 7.87388 14.7171 8.15449 14.4678C5.94245 14.2349 3.6151 13.3702 3.6151 9.51204C3.6151 8.41449 4.01102 7.51653 4.63837 6.81816C4.53939 6.56878 4.19265 5.53755 4.73755 4.15735C4.73755 4.15735 5.57939 3.89122 7.47755 5.18837C8.29022 4.9685 9.12832 4.85666 9.9702 4.85571C10.812 4.85571 11.6702 4.97224 12.4627 5.18837C14.361 3.89122 15.2029 4.15735 15.2029 4.15735C15.7478 5.53755 15.4008 6.56878 15.3018 6.81816C15.9457 7.51653 16.3253 8.41449 16.3253 9.51204C16.3253 13.3702 13.998 14.2182 11.7694 14.4678C12.1327 14.7837 12.4461 15.3822 12.4461 16.3302C12.4461 17.6771 12.4298 18.7582 12.4298 19.0906C12.4298 19.3567 12.6114 19.6729 13.1065 19.5731C17.0682 18.2424 19.9239 14.4843 19.9239 10.0443C19.9402 4.4898 15.4669 0 9.9702 0Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const YarnIcon = () => (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
focusable={false}
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
d="M19.0336 16.0116C18.0026 16.2564 17.4807 16.482 16.2049 17.3131C14.2075 18.6018 12.0297 19.201 12.0297 19.201C12.0297 19.201 11.8493 19.4716 11.3274 19.5941C10.4253 19.8131 7.02969 20 6.72041 20.0064C5.88923 20.0129 5.38021 19.7938 5.23845 19.4523C4.80675 18.4214 5.85701 17.9704 5.85701 17.9704C5.85701 17.9704 5.62505 17.8286 5.48974 17.6997C5.36732 17.5773 5.23845 17.3325 5.19979 17.4227C5.03871 17.8157 4.95495 18.7758 4.52325 19.2075C3.93046 19.8067 2.80933 19.6069 2.14567 19.259C1.41758 18.8724 2.19722 17.9639 2.19722 17.9639C2.19722 17.9639 1.80418 18.1959 1.48845 17.7191C1.20495 17.2809 0.940773 16.5335 1.01165 15.6121C1.10186 14.549 2.27454 13.5309 2.27454 13.5309C2.27454 13.5309 2.06835 11.9781 2.7449 10.3866C3.35701 8.93685 5.00649 7.77061 5.00649 7.77061C5.00649 7.77061 3.62119 6.24355 4.13665 4.85824C4.4717 3.95618 4.60701 3.96262 4.71655 3.92396C5.10314 3.77577 5.47686 3.61468 5.75392 3.31185C7.13923 1.817 8.90469 2.10051 8.90469 2.10051C8.90469 2.10051 9.72943 -0.431708 10.5026 0.0644255C10.741 0.219065 11.598 2.12628 11.598 2.12628C11.598 2.12628 12.5129 1.59149 12.616 1.79123C13.1702 2.86726 13.2346 4.92267 12.9897 6.17267C12.5774 8.23453 11.5464 9.34278 11.1341 10.0387C11.0374 10.1997 12.2423 10.7088 13.0026 12.8157C13.7049 14.7423 13.0799 16.3595 13.1895 16.5399C13.2088 16.5722 13.2153 16.585 13.2153 16.585C13.2153 16.585 14.0207 16.6495 15.6379 15.6508C16.5013 15.116 17.5258 14.5167 18.6921 14.5039C19.8196 14.4845 19.8776 15.8118 19.0336 16.0116Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from "./FigmaIcon";
|
||||
export { default as NavLogoWhite } from "./NavLogoWhite";
|
||||
export { default as NAVLogoDark } from "./NavLogoDark";
|
||||
|
||||
export * from "./Logo";
|
||||
export * from "./Icons";
|
||||
@@ -36,9 +36,9 @@ const Footer = () => {
|
||||
<li>
|
||||
<Link
|
||||
className="text-text-on-inverted focus:shadow-focus focus:text-text-default focus:bg-blue-200 focus:shadow-blue-200"
|
||||
href="https://nav-it.slack.com/archives/C0370ADS0HX"
|
||||
href="https://nav-it.slack.com/archives/C7NE7A8UF"
|
||||
>
|
||||
#Aksel på Slack
|
||||
#Aksel-designsystemet på Slack
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { FeedbackT } from "@/lib";
|
||||
import { IdContext } from "@/utils";
|
||||
import { Button, Heading, Label, Textarea } from "@navikt/ds-react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
|
||||
const FooterForm = () => {
|
||||
const idCtx = useContext(IdContext);
|
||||
|
||||
const [contactForm, setContactForm] = useState({
|
||||
content: "",
|
||||
hasWritten: false,
|
||||
@@ -27,12 +31,16 @@ const FooterForm = () => {
|
||||
if (fail) return;
|
||||
setContentError({ content: "" });
|
||||
|
||||
fetch("/api/generalFeedback", {
|
||||
const body: FeedbackT = {
|
||||
message: contactForm.content,
|
||||
url: `${basePath}${asPath}`,
|
||||
type: "footer",
|
||||
docId: idCtx?.id,
|
||||
};
|
||||
|
||||
fetch("/api/feedback", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
message: contactForm.content,
|
||||
url: `${basePath}${asPath}`,
|
||||
}),
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
setSent({ status: true });
|
||||
|
||||
@@ -54,15 +54,18 @@ const AkselArtikkelTemplate = ({
|
||||
: `Relevante artikler`}
|
||||
</Heading>
|
||||
<div className="card-grid-3-1 mt-6 px-4">
|
||||
{data.relevante_artikler.map((x: any) => (
|
||||
<ArtikkelCard {...x} key={x._id} />
|
||||
))}
|
||||
{data.relevante_artikler.map((x: any) =>
|
||||
x && x?._id ? <ArtikkelCard {...x} key={x._id} /> : null
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
const filteredTema =
|
||||
hasTema && data?.tema?.filter((x: any) => x?.title && x?.slug);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@@ -136,7 +139,7 @@ const AkselArtikkelTemplate = ({
|
||||
</div>
|
||||
{hasTema && (
|
||||
<div className="mt-8 flex flex-wrap gap-2">
|
||||
{data.tema.map(({ title, slug }: any) => (
|
||||
{filteredTema.map(({ title, slug }: any) => (
|
||||
<span key={title}>
|
||||
<NextLink
|
||||
key={title}
|
||||
|
||||
@@ -125,7 +125,14 @@ export const WithSidebar = ({
|
||||
</div>
|
||||
</div>
|
||||
{variant === "page" && pageProps.status?.bilde && (
|
||||
<div className="relative hidden aspect-square h-[12.5rem] lg:block xl:mr-40">
|
||||
<div
|
||||
className={cl(
|
||||
"relative hidden aspect-square h-[12.5rem] lg:block xl:mr-40",
|
||||
{
|
||||
"hue-rotate-[65deg]": pageProps?.status?.tag === "beta",
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Image
|
||||
src={urlFor(pageProps.status?.bilde).auto("format").url()}
|
||||
decoding="async"
|
||||
|
||||
@@ -63,7 +63,11 @@ const NavItem = ({
|
||||
: "info"
|
||||
}
|
||||
>
|
||||
{link.tag}
|
||||
{link.tag === "new"
|
||||
? "ny"
|
||||
: link.tag === "deprecated"
|
||||
? "Avviklet"
|
||||
: link.tag}
|
||||
</Tag>
|
||||
)}
|
||||
</span>
|
||||
@@ -130,7 +134,17 @@ export const Sidebar = ({
|
||||
...x,
|
||||
pages: links
|
||||
.filter((y) => y?.kategori === x.value)
|
||||
.sort((a, b) => a?.heading.localeCompare(b?.heading)),
|
||||
.sort((a, b) => {
|
||||
if (a?.tag === "deprecated" && b?.tag === "deprecated") {
|
||||
return 0;
|
||||
} else if (a?.tag === "deprecated") {
|
||||
return 1;
|
||||
} else if (b.tag === "deprecated") {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return a?.heading.localeCompare(b?.heading);
|
||||
}),
|
||||
}))
|
||||
.filter((x) => !(!x.pages || x.pages.length === 0)),
|
||||
[links, kategori]
|
||||
|
||||