mirror of
https://github.com/navikt/aksel.git
synced 2026-10-13 17:22:02 +00:00
Compare commits
111
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0f334061b | ||
|
|
79bddd9caa | ||
|
|
4a68409898 | ||
|
|
786bff67ab | ||
|
|
8a17caea6c | ||
|
|
3f43d0bf3b | ||
|
|
26541984ac | ||
|
|
10303602aa | ||
|
|
42254543d8 | ||
|
|
81c84e4817 | ||
|
|
b441175028 | ||
|
|
75362a0dc1 | ||
|
|
d8732ffa0e | ||
|
|
0ea9155085 | ||
|
|
92b9687ed1 | ||
|
|
1c022b14c9 | ||
|
|
abdeb616d2 | ||
|
|
37165e9f38 | ||
|
|
d19920ca78 | ||
|
|
60d3481300 | ||
|
|
f486937439 | ||
|
|
c62058abc1 | ||
|
|
84d9b8260f | ||
|
|
c108063dcb | ||
|
|
2a8533ad56 | ||
|
|
9b990a2178 | ||
|
|
5fec1f24f4 | ||
|
|
c27c1cfe26 | ||
|
|
5a649d2282 | ||
|
|
baac9cc251 | ||
|
|
d924d523e4 | ||
|
|
fc5a77081d | ||
|
|
1f3477a3e9 | ||
|
|
925450cb73 | ||
|
|
534a6a3b40 | ||
|
|
51eee7f011 | ||
|
|
909ae6aed1 | ||
|
|
cd3859a91e | ||
|
|
338f14e7ac | ||
|
|
aed1d082c3 | ||
|
|
31e9226b55 | ||
|
|
fe15b4d275 | ||
|
|
28e095d5a2 | ||
|
|
43377841ba | ||
|
|
2976ee9626 | ||
|
|
23c135b437 | ||
|
|
7f0498f250 | ||
|
|
5075c8d0a6 | ||
|
|
792eeaba42 | ||
|
|
0c7fc04935 | ||
|
|
0bb339e54a | ||
|
|
f5eb3adaea | ||
|
|
77cf71f814 | ||
|
|
f8c12b0757 | ||
|
|
be8265e13e | ||
|
|
0c7d6fb461 | ||
|
|
b97ac03c5a | ||
|
|
0ddb589138 | ||
|
|
1440c99b96 | ||
|
|
f8cf39c906 | ||
|
|
158c032436 | ||
|
|
6d6e6443dd | ||
|
|
e12ea8694e | ||
|
|
2d4cb67643 | ||
|
|
f5533766c8 | ||
|
|
3d1b0ac204 | ||
|
|
a1678d3b19 | ||
|
|
7007d3c58e | ||
|
|
8524346044 | ||
|
|
d5d8898659 | ||
|
|
1d635e1dbd | ||
|
|
6b3932b172 | ||
|
|
4d6ae1ae42 | ||
|
|
6c29fa83dd | ||
|
|
a6b6733861 | ||
|
|
3e54bd4f43 | ||
|
|
ec205fbc5f | ||
|
|
d8a4097c82 | ||
|
|
3a3a53f823 | ||
|
|
3d3e907608 | ||
|
|
986d0e9740 | ||
|
|
50bb7d4ff8 | ||
|
|
c2151e0514 | ||
|
|
4bc4dea85f | ||
|
|
6b2547cec5 | ||
|
|
5d4a46d1a8 | ||
|
|
cb8c7cabee | ||
|
|
7d675373bf | ||
|
|
e49b7ec875 | ||
|
|
c8cac2eafb | ||
|
|
99fca2a566 | ||
|
|
aa7f8ba1f8 | ||
|
|
70c5affbd3 | ||
|
|
d88a0c8aef | ||
|
|
dfd04f3ed7 | ||
|
|
a2d915cab9 | ||
|
|
e77568f58f | ||
|
|
fcbf6eada5 | ||
|
|
732a0491ea | ||
|
|
47984db6b1 | ||
|
|
4550973536 | ||
|
|
044aa09bc5 | ||
|
|
362c6d4116 | ||
|
|
1e77da3039 | ||
|
|
66ec09581c | ||
|
|
0f3066d87a | ||
|
|
ffd5470332 | ||
|
|
e9b98bb43c | ||
|
|
cf8572074c | ||
|
|
05e63c13af | ||
|
|
1178e96497 |
No files matched your search
@@ -20,7 +20,7 @@
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": ["shadow-dom", "aksel.nav.no", "aksel-icons-figma-plugin"],
|
||||
"ignore": ["aksel.nav.no", "aksel-icons-figma-plugin"],
|
||||
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
"updateInternalDependents": "always"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
!.storybook
|
||||
+5
-1
@@ -24,12 +24,14 @@ module.exports = {
|
||||
"error",
|
||||
{ propElementValues: "always" },
|
||||
],
|
||||
// TODO add: "object-shorthand": "error",
|
||||
"array-callback-return": "error",
|
||||
"object-shorthand": "error",
|
||||
// TODO: Consider adding the rule "id-length"
|
||||
// Temporary:
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
// "import/no-cycle": [1],
|
||||
},
|
||||
reportUnusedDisableDirectives: true,
|
||||
overrides: [
|
||||
@@ -37,6 +39,7 @@ module.exports = {
|
||||
files: ["**/*.ts?(x)"],
|
||||
extends: ["plugin:@typescript-eslint/recommended"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-shadow": ["error", { hoist: "all" }], // TODO: Consider { builtinGlobals: true }
|
||||
"@typescript-eslint/no-explicit-any": "off", // Temporary
|
||||
},
|
||||
},
|
||||
@@ -89,5 +92,6 @@ module.exports = {
|
||||
"cjs",
|
||||
"dist",
|
||||
"**/codemod/**/*.js",
|
||||
"!.storybook",
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Eksempel på bruk
|
||||
description: Send et eksempel på ting fra Aksel som er brukt i det fri (tokens, komponenter, maler, etc)
|
||||
title: "[Eksempel på bruk]: "
|
||||
labels: ["dokumentasjon 📖"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Takk for at du bidrar til Aksel!
|
||||
- type: input
|
||||
id: title
|
||||
attributes:
|
||||
label: Lenke til side
|
||||
description: Lenke til siden hvor eksemplet er
|
||||
placeholder:
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: examples
|
||||
attributes:
|
||||
label: Forklaring
|
||||
description: Forklar om det er noe man må gjøre for å se eksemplet, eller hvor på siden det ligger
|
||||
@@ -2,8 +2,6 @@ name: Ny komponent
|
||||
description: Send et ønske om en eller flere nye komponenter
|
||||
title: "[Ny komponent]: "
|
||||
labels: ["nytt ✨", "forespørsel 🥰", "komponenter 🧩"]
|
||||
assignees:
|
||||
- KenAJoh
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
@@ -2,8 +2,6 @@ name: Nytt ikon
|
||||
description: Send et ønske om et eller flere nye ikoner
|
||||
title: "[Nytt ikon]: "
|
||||
labels: ["nytt ✨", "ikoner 🖼", "forespørsel 🥰"]
|
||||
assignees:
|
||||
- KenAJoh
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
@@ -2,8 +2,6 @@ name: Innspill til komponent
|
||||
description: Send inn innspill til komponent
|
||||
title: "[Innspill til komponent]: "
|
||||
labels: ["forespørsel 🥰", "komponenter 🧩", "nytt ✨", "beta 🧪"]
|
||||
assignees:
|
||||
- KenAJoh
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
@@ -2,8 +2,6 @@ name: Innspill til ikon
|
||||
description: Send inn innspill til ikon
|
||||
title: "[Tilbakemelding på ikon]: "
|
||||
labels: ["forespørsel 🥰", "ikoner 🖼️"]
|
||||
assignees:
|
||||
- KenAJoh
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
+2
-1
@@ -151,4 +151,5 @@ playwright-report/
|
||||
backup-*.tar.gz
|
||||
sitemap-urls.json
|
||||
*.tar.gz
|
||||
searchindex.json
|
||||
searchindex.json
|
||||
aksel.nav.no/website/public/rss
|
||||
@@ -1,5 +1,47 @@
|
||||
# @navikt/aksel-icons
|
||||
|
||||
## 5.10.2
|
||||
|
||||
## 5.10.1
|
||||
|
||||
## 5.10.0
|
||||
|
||||
## 5.9.2
|
||||
|
||||
## 5.9.1
|
||||
|
||||
## 5.9.0
|
||||
|
||||
## 5.8.0
|
||||
|
||||
## 5.7.6
|
||||
|
||||
## 5.7.5
|
||||
|
||||
## 5.7.4
|
||||
|
||||
## 5.7.3
|
||||
|
||||
## 5.7.2
|
||||
|
||||
## 5.7.1
|
||||
|
||||
## 5.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Ikoner: Nye ikoner `ChevronLeftFirstCircle` og `ChevronRightLastCircle` ([#2365](https://github.com/navikt/aksel/pull/2365))
|
||||
|
||||
## 5.6.5
|
||||
|
||||
## 5.6.4
|
||||
|
||||
## 5.6.3
|
||||
|
||||
## 5.6.2
|
||||
|
||||
## 5.6.1
|
||||
|
||||
## 5.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"postcss": "^8.4.0",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-url": "^10.1.3",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.2.0",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3.75C7.44365 3.75 3.75 7.44365 3.75 12C3.75 16.5563 7.44365 20.25 12 20.25C16.5563 20.25 20.25 16.5563 20.25 12C20.25 7.44365 16.5563 3.75 12 3.75ZM2.25 12C2.25 6.61522 6.61522 2.25 12 2.25C17.3848 2.25 21.75 6.61522 21.75 12C21.75 17.3848 17.3848 21.75 12 21.75C6.61522 21.75 2.25 17.3848 2.25 12ZM16.0303 16.0303C15.7374 16.3232 15.2626 16.3232 14.9697 16.0303L11.4697 12.5303C11.1768 12.2374 11.1768 11.7626 11.4697 11.4697L14.9697 7.96967C15.2626 7.67678 15.7374 7.67678 16.0303 7.96967C16.3232 8.26256 16.3232 8.73744 16.0303 9.03033L13.0607 12L16.0303 14.9697C16.3232 15.2626 16.3232 15.7374 16.0303 16.0303ZM9.75 8.5C9.75 8.08579 9.41421 7.75 9 7.75C8.58579 7.75 8.25 8.08579 8.25 8.5V15.5C8.25 15.9142 8.58579 16.25 9 16.25C9.41421 16.25 9.75 15.9142 9.75 15.5V8.5Z" fill="#23262A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 949 B |
@@ -0,0 +1,11 @@
|
||||
name: ChevronLeftFirstCircle
|
||||
category: Arrows
|
||||
sub_category: Chevron
|
||||
keywords:
|
||||
- left
|
||||
- venstre
|
||||
- side
|
||||
- første
|
||||
variant: Stroke
|
||||
updated_at: 11.10.2023
|
||||
created_at: 11.10.2023
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.75 12C21.75 17.3848 17.3848 21.75 12 21.75C6.61522 21.75 2.25 17.3848 2.25 12C2.25 6.61522 6.61522 2.25 12 2.25C17.3848 2.25 21.75 6.61522 21.75 12ZM16.2803 16.2803C15.9874 16.5732 15.5126 16.5732 15.2197 16.2803L11.428 12.4887C11.1351 12.1958 11.1351 11.7209 11.428 11.428L15.2197 7.63634C15.5126 7.34344 15.9874 7.34344 16.2803 7.63634C16.5732 7.92923 16.5732 8.4041 16.2803 8.697L13.019 11.9583L16.2803 15.2197C16.5732 15.5126 16.5732 15.9874 16.2803 16.2803ZM9.75 8.25C9.75 7.83579 9.41421 7.5 9 7.5C8.58579 7.5 8.25 7.83579 8.25 8.25V15.8333C8.25 16.2475 8.58579 16.5833 9 16.5833C9.41421 16.5833 9.75 16.2475 9.75 15.8333V8.25Z" fill="#23262A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 809 B |
@@ -0,0 +1,11 @@
|
||||
name: ChevronLeftFirstCircleFill
|
||||
category: Arrows
|
||||
sub_category: Chevron
|
||||
keywords:
|
||||
- left
|
||||
- venstre
|
||||
- side
|
||||
- første
|
||||
variant: Fill
|
||||
updated_at: 11.10.2023
|
||||
created_at: 11.10.2023
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3.75C7.44365 3.75 3.75 7.44365 3.75 12C3.75 16.5563 7.44365 20.25 12 20.25C16.5563 20.25 20.25 16.5563 20.25 12C20.25 7.44365 16.5563 3.75 12 3.75ZM2.25 12C2.25 6.61522 6.61522 2.25 12 2.25C17.3848 2.25 21.75 6.61522 21.75 12C21.75 17.3848 17.3848 21.75 12 21.75C6.61522 21.75 2.25 17.3848 2.25 12ZM7.96967 7.96967C8.26256 7.67678 8.73744 7.67678 9.03033 7.96967L12.5303 11.4697C12.8232 11.7626 12.8232 12.2374 12.5303 12.5303L9.03033 16.0303C8.73744 16.3232 8.26256 16.3232 7.96967 16.0303C7.67678 15.7374 7.67678 15.2626 7.96967 14.9697L10.9393 12L7.96967 9.03033C7.67678 8.73744 7.67678 8.26256 7.96967 7.96967ZM15.75 8.55C15.75 8.13579 15.4142 7.8 15 7.8C14.5858 7.8 14.25 8.13579 14.25 8.55V15.55C14.25 15.9642 14.5858 16.3 15 16.3C15.4142 16.3 15.75 15.9642 15.75 15.55V8.55Z" fill="#23262A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 955 B |
@@ -0,0 +1,11 @@
|
||||
name: ChevronRightLastCircle
|
||||
category: Arrows
|
||||
sub_category: Chevron
|
||||
keywords:
|
||||
- right
|
||||
- høyre
|
||||
- side
|
||||
- siste
|
||||
variant: Stroke
|
||||
updated_at: 11.10.2023
|
||||
created_at: 11.10.2023
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.75 12C21.75 17.3848 17.3848 21.75 12 21.75C6.61522 21.75 2.25 17.3848 2.25 12C2.25 6.61522 6.61522 2.25 12 2.25C17.3848 2.25 21.75 6.61522 21.75 12ZM7.71967 7.71967C8.01256 7.42678 8.48744 7.42678 8.78033 7.71967L12.572 11.5113C12.8649 11.8042 12.8649 12.2791 12.572 12.572L8.78033 16.3637C8.48744 16.6566 8.01256 16.6566 7.71967 16.3637C7.42678 16.0708 7.42678 15.5959 7.71967 15.303L10.981 12.0417L7.71967 8.78033C7.42678 8.48744 7.42678 8.01256 7.71967 7.71967ZM15.75 8.25C15.75 7.83579 15.4142 7.5 15 7.5C14.5858 7.5 14.25 7.83579 14.25 8.25V15.8333C14.25 16.2475 14.5858 16.5833 15 16.5833C15.4142 16.5833 15.75 16.2475 15.75 15.8333V8.25Z" fill="#23262A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 820 B |
@@ -0,0 +1,11 @@
|
||||
name: ChevronRightLastCircleFill
|
||||
category: Arrows
|
||||
sub_category: Chevron
|
||||
keywords:
|
||||
- right
|
||||
- høyre
|
||||
- side
|
||||
- siste
|
||||
variant: Fill
|
||||
updated_at: 11.10.2023
|
||||
created_at: 11.10.2023
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-icons",
|
||||
"version": "5.6.0",
|
||||
"version": "5.10.2",
|
||||
"author": "Aksel | NAV",
|
||||
"homepage": "https://aksel.nav.no/ikoner",
|
||||
"repository": {
|
||||
|
||||
@@ -1,5 +1,43 @@
|
||||
# @navikt/aksel-stylelint
|
||||
|
||||
## 5.10.2
|
||||
|
||||
## 5.10.1
|
||||
|
||||
## 5.10.0
|
||||
|
||||
## 5.9.2
|
||||
|
||||
## 5.9.1
|
||||
|
||||
## 5.9.0
|
||||
|
||||
## 5.8.0
|
||||
|
||||
## 5.7.6
|
||||
|
||||
## 5.7.5
|
||||
|
||||
## 5.7.4
|
||||
|
||||
## 5.7.3
|
||||
|
||||
## 5.7.2
|
||||
|
||||
## 5.7.1
|
||||
|
||||
## 5.7.0
|
||||
|
||||
## 5.6.5
|
||||
|
||||
## 5.6.4
|
||||
|
||||
## 5.6.3
|
||||
|
||||
## 5.6.2
|
||||
|
||||
## 5.6.1
|
||||
|
||||
## 5.6.0
|
||||
|
||||
## 5.5.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-stylelint",
|
||||
"version": "5.6.0",
|
||||
"version": "5.10.2",
|
||||
"author": "Aksel | NAV",
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
|
||||
"repository": {
|
||||
@@ -35,8 +35,8 @@
|
||||
"dev": "yarn watch:lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-css": "^5.6.0",
|
||||
"@navikt/ds-tokens": "^5.6.0",
|
||||
"@navikt/ds-css": "^5.10.2",
|
||||
"@navikt/ds-tokens": "^5.10.2",
|
||||
"@types/jest": "^29.0.0",
|
||||
"concurrently": "7.2.1",
|
||||
"copyfiles": "2.4.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import stylelint from "stylelint";
|
||||
import valueParser from "postcss-value-parser";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { Node as PostCSSNode } from "postcss";
|
||||
import valueParser from "postcss-value-parser";
|
||||
import stylelint from "stylelint";
|
||||
|
||||
import { isCustomProperty, tokenExists } from "../../utils";
|
||||
|
||||
@@ -34,7 +34,6 @@ export const messages = stylelint.utils.ruleMessages(ruleName, {
|
||||
});
|
||||
|
||||
const checkDeclValue = (
|
||||
controlledPrefixes: string[],
|
||||
value: string,
|
||||
postcssResult: stylelint.PostcssResult,
|
||||
rootNode: PostCSSNode
|
||||
@@ -58,7 +57,6 @@ const checkDeclValue = (
|
||||
};
|
||||
|
||||
const checkDeclProp = (
|
||||
controlledPrefixes: string[],
|
||||
prop: string,
|
||||
postcssResult: stylelint.PostcssResult,
|
||||
rootNode: PostCSSNode
|
||||
@@ -81,8 +79,8 @@ const checkDeclProp = (
|
||||
const ruleFunction: stylelint.Rule = () => {
|
||||
return (postcssRoot, postcssResult) => {
|
||||
postcssRoot.walkDecls((node) => {
|
||||
checkDeclValue(controlledPrefixes, node.value, postcssResult, node);
|
||||
checkDeclProp(controlledPrefixes, node.prop, postcssResult, node);
|
||||
checkDeclValue(node.value, postcssResult, node);
|
||||
checkDeclProp(node.prop, postcssResult, node);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -92,16 +92,6 @@ const tokenJsonFile = "./tokens.json";
|
||||
|
||||
const allowedTokenNames = [];
|
||||
|
||||
export const addTokens = (
|
||||
tokenJSONFile: string,
|
||||
allowedTokenNames: string[]
|
||||
) => {
|
||||
const jsonFileBuffer = readFileSync(`${__dirname}/${tokenJSONFile}`);
|
||||
const fileString = jsonFileBuffer.toString();
|
||||
const flattened = flattenObject(JSON.parse(fileString));
|
||||
flattened.forEach((token) => allowedTokenNames.push(token));
|
||||
};
|
||||
|
||||
export const tokenExists = (
|
||||
controlledPrefixes: string[],
|
||||
inputToken: string
|
||||
@@ -110,7 +100,6 @@ export const tokenExists = (
|
||||
if (!allowedTokenNames.length) {
|
||||
const cssFileBuffer = readFileSync(`${__dirname}/${tokenCSSFile}`);
|
||||
const cssFileString = cssFileBuffer.toString();
|
||||
|
||||
valueParser(cssFileString).walk((node) => {
|
||||
if (
|
||||
node.type === "word" &&
|
||||
@@ -121,7 +110,9 @@ export const tokenExists = (
|
||||
}
|
||||
});
|
||||
|
||||
addTokens(tokenJsonFile, allowedTokenNames);
|
||||
const jsonFileBuffer = readFileSync(`${__dirname}/${tokenJsonFile}`);
|
||||
const flattened = flattenObject(JSON.parse(jsonFileBuffer.toString()));
|
||||
flattened.forEach((token) => allowedTokenNames.push(token));
|
||||
}
|
||||
|
||||
return allowedTokenNames.includes(inputToken);
|
||||
|
||||
@@ -1,5 +1,138 @@
|
||||
# @navikt/aksel
|
||||
|
||||
## 5.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`79bddd9ca`](https://github.com/navikt/aksel/commit/79bddd9caa9467b60e1e0c1c15b16f76dccbc304)]:
|
||||
- @navikt/ds-css@5.10.2
|
||||
|
||||
## 5.10.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.10.1
|
||||
|
||||
## 5.10.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`60d348130`](https://github.com/navikt/aksel/commit/60d34813009377a1a7d84bed6627c60524849957)]:
|
||||
- @navikt/ds-css@5.10.0
|
||||
|
||||
## 5.9.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.9.2
|
||||
|
||||
## 5.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`925450cb7`](https://github.com/navikt/aksel/commit/925450cb73cd792cd3b6ad3ebf9f38ba022ee4df)]:
|
||||
- @navikt/ds-css@5.9.1
|
||||
|
||||
## 5.9.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`51eee7f01`](https://github.com/navikt/aksel/commit/51eee7f011aa016973bb952313cf65a1c55aa382), [`51eee7f01`](https://github.com/navikt/aksel/commit/51eee7f011aa016973bb952313cf65a1c55aa382), [`51eee7f01`](https://github.com/navikt/aksel/commit/51eee7f011aa016973bb952313cf65a1c55aa382), [`51eee7f01`](https://github.com/navikt/aksel/commit/51eee7f011aa016973bb952313cf65a1c55aa382)]:
|
||||
- @navikt/ds-css@5.9.0
|
||||
|
||||
## 5.8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`0c7fc0493`](https://github.com/navikt/aksel/commit/0c7fc04935b96946b8ed1178c700077b6a1fcefd)]:
|
||||
- @navikt/ds-css@5.8.0
|
||||
|
||||
## 5.7.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6d6e6443d`](https://github.com/navikt/aksel/commit/6d6e6443dd00464183b03fad6f822e5f3efab100)]:
|
||||
- @navikt/ds-css@5.7.6
|
||||
|
||||
## 5.7.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`3d1b0ac20`](https://github.com/navikt/aksel/commit/3d1b0ac2041bbc289536fa5a0dc65b2170dfc6fa)]:
|
||||
- @navikt/ds-css@5.7.5
|
||||
|
||||
## 5.7.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.7.4
|
||||
|
||||
## 5.7.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`3d3e90760`](https://github.com/navikt/aksel/commit/3d3e9076088c5e72ed89fb97109b75dcf6e09d96), [`d8a4097c8`](https://github.com/navikt/aksel/commit/d8a4097c82ceee95bd216b36e1dc8eb99e75d7ee), [`a6b673386`](https://github.com/navikt/aksel/commit/a6b67338612b7944dc4e6b3d541244f521e94500)]:
|
||||
- @navikt/ds-css@5.7.3
|
||||
|
||||
## 5.7.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.7.2
|
||||
|
||||
## 5.7.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`cb8c7cabe`](https://github.com/navikt/aksel/commit/cb8c7cabeec6ddcf08c06debd7ca889888b1f5e6)]:
|
||||
- @navikt/ds-css@5.7.1
|
||||
|
||||
## 5.7.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`70c5affbd`](https://github.com/navikt/aksel/commit/70c5affbd3bfb245e905043e7d3ae1c97eeef5e3)]:
|
||||
- @navikt/ds-css@5.7.0
|
||||
|
||||
## 5.6.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.6.5
|
||||
|
||||
## 5.6.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.6.4
|
||||
|
||||
## 5.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`66ec09581`](https://github.com/navikt/aksel/commit/66ec09581c6350ab1bccd473512b29d303ef75f5)]:
|
||||
- @navikt/ds-css@5.6.3
|
||||
|
||||
## 5.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.6.2
|
||||
|
||||
## 5.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@5.6.1
|
||||
|
||||
## 5.6.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel",
|
||||
"version": "5.6.0",
|
||||
"version": "5.10.2",
|
||||
"description": "Aksel command line interface. Handles css-imports, codemods and more",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -29,8 +29,8 @@
|
||||
},
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "5.6.0",
|
||||
"axios": "1.3.6",
|
||||
"@navikt/ds-css": "5.10.2",
|
||||
"axios": "1.6.0",
|
||||
"chalk": "4.1.0",
|
||||
"clipboardy": "^2.3.0",
|
||||
"commander": "10.0.1",
|
||||
@@ -38,7 +38,7 @@
|
||||
"fast-glob": "3.2.11",
|
||||
"figlet": "1.6.0",
|
||||
"is-git-clean": "1.1.0",
|
||||
"jscodeshift": "0.13.1",
|
||||
"jscodeshift": "^0.15.1",
|
||||
"jscodeshift-add-imports": "1.0.10",
|
||||
"lodash": "4.17.21",
|
||||
"react-scanner": "^1.1.0"
|
||||
|
||||
@@ -111,9 +111,9 @@ export function getMigrationNames() {
|
||||
export function getMigrationString() {
|
||||
let str = "";
|
||||
|
||||
Object.entries(migrations).forEach(([version, migrations]) => {
|
||||
Object.entries(migrations).forEach(([version, vMigrations]) => {
|
||||
str += `\n${chalk.underline(version)}\n`;
|
||||
migrations.forEach((migration) => {
|
||||
vMigrations.forEach((migration) => {
|
||||
str += `${chalk.blue(migration.value)}: ${migration.description}\n`;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,10 +13,10 @@ export default function transformer(file, api) {
|
||||
function addMigrationTag(node) {
|
||||
const attributes = node.openingElement.attributes;
|
||||
const isMigrated = attributes.find(
|
||||
(node) =>
|
||||
node.type === "JSXAttribute" &&
|
||||
node.name.name === "data-version" &&
|
||||
node.value.value === "v1"
|
||||
(attr) =>
|
||||
attr.type === "JSXAttribute" &&
|
||||
attr.name.name === "data-version" &&
|
||||
attr.value.value === "v1"
|
||||
);
|
||||
|
||||
if (!isMigrated) {
|
||||
@@ -30,7 +30,7 @@ export default function transformer(file, api) {
|
||||
function addExplicitStandardProp(node) {
|
||||
const attributes = node.openingElement.attributes;
|
||||
const variant = attributes.find(
|
||||
(node) => node.type === "JSXAttribute" && node.name.name === "size"
|
||||
(attr) => attr.type === "JSXAttribute" && attr.name.name === "size"
|
||||
);
|
||||
|
||||
if (!variant) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { JSCodeshift } from "jscodeshift";
|
||||
import { tokens } from "../../../tokens-map.mjs";
|
||||
import {
|
||||
getImportSpecifier,
|
||||
getImportSpecifierName,
|
||||
renameImportSpecifier,
|
||||
} from "../../../utils/imports";
|
||||
import { translateToken } from "../../../utils/translate-token";
|
||||
import { tokens } from "../../../tokens-map.mjs";
|
||||
import { getLineTerminator } from "../../../utils/lineterminator";
|
||||
import { translateToken } from "../../../utils/translate-token";
|
||||
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
@@ -29,9 +29,9 @@ export default function transformer(file: JSCodeshift, api) {
|
||||
return src;
|
||||
}
|
||||
|
||||
tokens.forEach((x) => {
|
||||
const name = translateToken(x[0], "js");
|
||||
const out = translateToken(x[1], "js");
|
||||
tokens.forEach((token) => {
|
||||
const name = translateToken(token[0], "js");
|
||||
const out = translateToken(token[1], "js");
|
||||
|
||||
let foundName: string = "";
|
||||
|
||||
|
||||
@@ -1,5 +1,97 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 5.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Primtives: CSS-import for primitives er nå mer spesifikk ([#2469](https://github.com/navikt/aksel/pull/2469))
|
||||
|
||||
## 5.10.1
|
||||
|
||||
## 5.10.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Primitives: Ny komponent `Page` og `Page.Block` ([#2456](https://github.com/navikt/aksel/pull/2456))
|
||||
|
||||
## 5.9.2
|
||||
|
||||
## 5.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :lipstick: Modal: Bedre håndtering av mobiler i landskapsmodus ([#2444](https://github.com/navikt/aksel/pull/2444))
|
||||
|
||||
## 5.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Datepicker/Monthpicker: Hvis man bruker komponentene i Modal vil Popover bli erstattet med Modal uansett om man er på desktop eller mobil. ([#2419](https://github.com/navikt/aksel/pull/2419))
|
||||
|
||||
- Datepicker/Monthpicker/Popover: Fjernet `bubbleEscape`-prop. ([#2419](https://github.com/navikt/aksel/pull/2419))
|
||||
|
||||
- useDatepicker/useMonthPicker/useRangedpicker: Fjernet `openOnFocus`-prop, kan nå bare åpnes ved klikk på date-knapp i input. ([#2419](https://github.com/navikt/aksel/pull/2419))
|
||||
|
||||
- Datepicker/Monthpicker: Bytter nå automatisk til Modalvisning på mobil. ([#2419](https://github.com/navikt/aksel/pull/2419))
|
||||
|
||||
## 5.8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :bug: Modal: Bedre støtte for Tooltip i Modal ([#2429](https://github.com/navikt/aksel/pull/2429))
|
||||
|
||||
## 5.7.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :lipstick: Chips.Toggle: Ikon i uvalgt tilstand ([#2395](https://github.com/navikt/aksel/pull/2395))
|
||||
|
||||
## 5.7.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Checkbox, Radio: Transparent-border fungerer nå bedre på mørkere bakgrunner ([#2388](https://github.com/navikt/aksel/pull/2388))
|
||||
|
||||
## 5.7.4
|
||||
|
||||
## 5.7.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Ny spacing token på 0.375rem ([#2370](https://github.com/navikt/aksel/pull/2370))
|
||||
|
||||
- Select: Tokenstøtte for å endre chevronfarge sammen med tekst ([#2383](https://github.com/navikt/aksel/pull/2383))
|
||||
|
||||
- HelpText: Knapp er visuelt mindre. Klikkflate er fortsatt 24px ([#2376](https://github.com/navikt/aksel/pull/2376))
|
||||
|
||||
## 5.7.2
|
||||
|
||||
## 5.7.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Radio: readonly håndterer nå hover-state bedre ([#2363](https://github.com/navikt/aksel/pull/2363))
|
||||
|
||||
## 5.7.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :lipstick: CopyButton: Justert padding, gap og animasjon ([#2355](https://github.com/navikt/aksel/pull/2355))
|
||||
|
||||
## 5.6.5
|
||||
|
||||
## 5.6.4
|
||||
|
||||
## 5.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :bug: Modal: Sett riktig tekstfarge ([#2340](https://github.com/navikt/aksel/pull/2340))
|
||||
|
||||
## 5.6.2
|
||||
|
||||
## 5.6.1
|
||||
|
||||
## 5.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--a-spacing-1);
|
||||
gap: var(--a-spacing-05);
|
||||
margin: 0;
|
||||
padding: 0 var(--a-spacing-3);
|
||||
text-decoration: none;
|
||||
@@ -97,6 +97,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
.navds-chips--medium .navds-chips__toggle--with-checkmark {
|
||||
padding-left: var(--a-spacing-1-alt);
|
||||
}
|
||||
|
||||
.navds-chips--small .navds-chips__toggle--with-checkmark {
|
||||
padding-left: var(--a-spacing-1);
|
||||
}
|
||||
|
||||
.navds-chips--small .navds-chips__toggle-icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.navds-chips__removable {
|
||||
gap: 0;
|
||||
}
|
||||
@@ -160,11 +173,11 @@
|
||||
}
|
||||
|
||||
.navds-chips--icon-left {
|
||||
padding-left: 0.375rem;
|
||||
padding-left: var(--a-spacing-1-alt);
|
||||
}
|
||||
|
||||
.navds-chips--icon-right {
|
||||
padding-right: 0.375rem;
|
||||
padding-right: var(--a-spacing-1-alt);
|
||||
}
|
||||
|
||||
.navds-chips--small .navds-chips--icon-right {
|
||||
|
||||
@@ -35,6 +35,10 @@ const StyleMappings = {
|
||||
component: "Box",
|
||||
main: primitivesCss,
|
||||
},
|
||||
{
|
||||
component: "Page",
|
||||
main: primitivesCss,
|
||||
},
|
||||
{
|
||||
component: "HGrid",
|
||||
main: primitivesCss,
|
||||
@@ -183,7 +187,7 @@ const StyleMappings = {
|
||||
{
|
||||
component: "DatePicker",
|
||||
main: "date.css",
|
||||
dependencies: [typoCss, primitivesCss, "button.css", "popover.css"],
|
||||
dependencies: [typoCss, "button.css", "popover.css", primitivesCss],
|
||||
},
|
||||
{
|
||||
component: "MonthPicker",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
}
|
||||
|
||||
.navds-copybutton {
|
||||
--__ac-copybutton-padding: var(--a-spacing-3) var(--a-spacing-5);
|
||||
--__ac-copybutton-padding: var(--a-spacing-3) var(--a-spacing-5) var(--a-spacing-3) var(--a-spacing-4);
|
||||
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
@@ -28,18 +28,30 @@
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
.navds-copybutton--icon-right {
|
||||
--__ac-copybutton-padding: var(--a-spacing-3) var(--a-spacing-4) var(--a-spacing-3) var(--a-spacing-5);
|
||||
}
|
||||
|
||||
.navds-copybutton--small {
|
||||
--__ac-copybutton-padding: var(--a-spacing-1) var(--a-spacing-3);
|
||||
--__ac-copybutton-padding: var(--a-spacing-1) var(--a-spacing-3) var(--a-spacing-1) var(--a-spacing-2);
|
||||
|
||||
min-height: 2rem;
|
||||
}
|
||||
|
||||
.navds-copybutton--small.navds-copybutton--icon-right {
|
||||
--__ac-copybutton-padding: var(--a-spacing-1) var(--a-spacing-2) var(--a-spacing-1) var(--a-spacing-3);
|
||||
}
|
||||
|
||||
.navds-copybutton--xsmall {
|
||||
--__ac-copybutton-padding: var(--a-spacing-05) var(--a-spacing-2);
|
||||
--__ac-copybutton-padding: var(--a-spacing-05) var(--a-spacing-2) var(--a-spacing-05) var(--a-spacing-1);
|
||||
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
|
||||
.navds-copybutton--xsmall.navds-copybutton--icon-right {
|
||||
--__ac-copybutton-padding: var(--a-spacing-05) var(--a-spacing-1) var(--a-spacing-05) var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-copybutton--icon-only {
|
||||
--__ac-copybutton-padding: var(--a-spacing-3);
|
||||
}
|
||||
@@ -73,6 +85,28 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.navds-copybutton--active .navds-copybutton__icon {
|
||||
animation: akselCopyButtonIconAnimation 2s cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
|
||||
@keyframes akselCopyButtonIconAnimation {
|
||||
8% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
17% {
|
||||
transform: translateY(-10%);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateY(2%);
|
||||
}
|
||||
|
||||
30% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
:where(.navds-copybutton--small, .navds-copybutton--xsmall):where(:not(:only-child)) {
|
||||
margin: -0.125rem;
|
||||
}
|
||||
@@ -140,6 +174,10 @@
|
||||
gap: var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-copybutton--small > .navds-copybutton__content {
|
||||
gap: var(--a-spacing-1-alt);
|
||||
}
|
||||
|
||||
.navds-copybutton--xsmall > .navds-copybutton__content {
|
||||
gap: var(--a-spacing-1);
|
||||
}
|
||||
|
||||
+26
-18
@@ -149,18 +149,6 @@
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
/* Focus layering */
|
||||
.navds-date__field-input:focus-visible,
|
||||
.navds-date__field-button {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-date__field-input:focus {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.navds-date .rdp-day_selected,
|
||||
.navds-monthpicker__month--selected {
|
||||
color: var(--ac-date-selected-text, var(--a-text-on-action));
|
||||
@@ -201,11 +189,11 @@
|
||||
}
|
||||
|
||||
.navds-date__field-input {
|
||||
padding-right: var(--a-spacing-14);
|
||||
padding-right: var(--a-spacing-12);
|
||||
}
|
||||
|
||||
.navds-form-field--small .navds-date__field-input {
|
||||
padding-right: var(--a-spacing-10);
|
||||
padding-right: var(--a-spacing-8);
|
||||
}
|
||||
|
||||
/* Error-handling */
|
||||
@@ -282,10 +270,6 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.navds-date__popover:where(.navds-popover) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Readonly */
|
||||
.navds-date__field--readonly .navds-date__field-button {
|
||||
cursor: default;
|
||||
@@ -310,11 +294,35 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.navds-date__modal.navds-date {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navds-date__modal-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
padding: var(--a-spacing-4);
|
||||
gap: var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-date__modal-body > .navds-date {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navds-date__popover:where(.navds-popover) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
.navds-date {
|
||||
padding: var(--a-spacing-5) var(--a-spacing-4);
|
||||
}
|
||||
|
||||
.navds-date__modal-body {
|
||||
padding: var(--a-spacing-6);
|
||||
}
|
||||
|
||||
.navds-date__caption {
|
||||
gap: var(--a-spacing-2);
|
||||
}
|
||||
|
||||
@@ -31,11 +31,13 @@
|
||||
.navds-radio__label::before {
|
||||
content: "";
|
||||
background-color: var(--ac-radio-checkbox-bg, var(--a-surface-default));
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-border, var(--a-border-default));
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: var(--a-border-radius-medium);
|
||||
border-radius: var(--a-border-radius-small);
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-border, var(--a-border-default));
|
||||
width: calc(1.5rem - 0.25rem);
|
||||
height: calc(1.5rem - 0.25rem);
|
||||
margin-block: 2px;
|
||||
margin-inline: 2px;
|
||||
}
|
||||
|
||||
.navds-radio__label::before {
|
||||
@@ -59,41 +61,42 @@
|
||||
|
||||
.navds-checkbox--small > .navds-checkbox__label,
|
||||
.navds-radio--small > .navds-radio__label {
|
||||
padding: 0.375rem 0;
|
||||
padding: var(--a-spacing-1-alt) 0;
|
||||
}
|
||||
|
||||
.navds-checkbox--small > .navds-checkbox__label::before,
|
||||
.navds-radio--small > .navds-radio__label::before {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
width: calc(1.25rem - 0.25rem);
|
||||
height: calc(1.25rem - 0.25rem);
|
||||
}
|
||||
|
||||
.navds-checkbox__input:focus-visible + .navds-checkbox__label::before,
|
||||
.navds-radio__input:focus-visible + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-border, var(--a-border-default)), var(--a-shadow-focus);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-border, var(--a-border-default)),
|
||||
0 0 0 2px var(--ac-radio-checkbox-bg, var(--a-surface-default)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-checkbox__input:focus + .navds-checkbox__label::before,
|
||||
.navds-radio__input:focus + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-border, var(--a-border-default)), var(--a-shadow-focus);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-border, var(--a-border-default)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
.navds-checkbox__input:hover:focus-visible + .navds-checkbox__label::before,
|
||||
.navds-radio__input:hover:focus-visible + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)), var(--a-shadow-focus);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-checkbox__input:hover:focus + .navds-checkbox__label::before,
|
||||
.navds-radio__input:hover:focus + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)), var(--a-shadow-focus);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
.navds-checkbox__input:indeterminate + .navds-checkbox__label::before {
|
||||
box-shadow: none;
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
background-color: var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
}
|
||||
|
||||
@@ -101,7 +104,7 @@
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(0.375rem, -50%);
|
||||
transform: translate(var(--a-spacing-1-alt), -50%);
|
||||
background-color: var(--ac-radio-checkbox-bg, var(--a-surface-default));
|
||||
width: 0.75rem;
|
||||
height: 0.25rem;
|
||||
@@ -114,7 +117,7 @@
|
||||
}
|
||||
|
||||
.navds-checkbox__input:checked + .navds-checkbox__label::before {
|
||||
box-shadow: none;
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
background-color: var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
}
|
||||
|
||||
@@ -126,7 +129,7 @@
|
||||
color: var(--ac-radio-checkbox-bg, var(--a-surface-default));
|
||||
position: absolute;
|
||||
height: 1.5rem;
|
||||
transform: translate(0.375rem);
|
||||
transform: translate(var(--a-spacing-1-alt));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -146,31 +149,33 @@
|
||||
|
||||
.navds-checkbox__input:indeterminate:focus-visible + .navds-checkbox__label::before,
|
||||
.navds-checkbox__input:checked:focus-visible + .navds-checkbox__label::before {
|
||||
box-shadow: inset 0 0 0 1px var(--ac-radio-checkbox-bg, var(--a-surface-default)), var(--a-shadow-focus);
|
||||
box-shadow: 0 0 0 1px var(--ac-radio-checkbox-action, var(--a-surface-action)),
|
||||
0 0 0 2px var(--ac-radio-checkbox-bg, var(--a-surface-default)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-checkbox__input:indeterminate:focus + .navds-checkbox__label::before,
|
||||
.navds-checkbox__input:checked:focus + .navds-checkbox__label::before {
|
||||
box-shadow: inset 0 0 0 1px var(--ac-radio-checkbox-bg, var(--a-surface-default)), var(--a-shadow-focus);
|
||||
box-shadow: 0 0 0 1px var(--ac-radio-checkbox-action, var(--a-surface-action)),
|
||||
0 0 0 2px var(--ac-radio-checkbox-bg, var(--a-surface-default)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
.navds-radio__input:checked + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)),
|
||||
inset 0 0 0 4px var(--ac-radio-checkbox-bg, var(--a-surface-default));
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)),
|
||||
inset 0 0 0 2px var(--ac-radio-checkbox-bg, var(--a-surface-default));
|
||||
background-color: var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
}
|
||||
|
||||
.navds-radio__input:checked:focus-visible + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)),
|
||||
inset 0 0 0 4px var(--ac-radio-checkbox-bg, var(--a-surface-default)), var(--a-shadow-focus);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)),
|
||||
inset 0 0 0 2px var(--ac-radio-checkbox-bg, var(--a-surface-default)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-radio__input:checked:focus + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)),
|
||||
inset 0 0 0 4px var(--ac-radio-checkbox-bg, var(--a-surface-default)), var(--a-shadow-focus);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action)),
|
||||
inset 0 0 0 2px var(--ac-radio-checkbox-bg, var(--a-surface-default)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,13 +187,13 @@
|
||||
.navds-checkbox__input:hover:not(:disabled):not(:checked):not(:indeterminate):not(:focus-visible)
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio__input:hover:not(:disabled):not(:checked):not(:focus-visible) + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-checkbox__input:hover:not(:disabled):not(:checked):not(:indeterminate):not(:focus) + .navds-checkbox__label::before,
|
||||
.navds-radio__input:hover:not(:disabled):not(:checked):not(:focus) + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,14 +206,14 @@
|
||||
> .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) + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger));
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger));
|
||||
}
|
||||
|
||||
.navds-checkbox--error
|
||||
> .navds-checkbox__input:focus-visible:not(:hover):not(:disabled):not(:checked):not(:indeterminate)
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio--error > .navds-radio__input:focus-visible: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);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
@@ -216,7 +221,7 @@
|
||||
> .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) + .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);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +230,7 @@
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio--error > .navds-radio__input:hover:not(:disabled):not(:checked):not(:focus-visible) + .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));
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger));
|
||||
}
|
||||
|
||||
.navds-checkbox--error
|
||||
@@ -233,7 +238,7 @@
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio--error > .navds-radio__input:focus-visible: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);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
@@ -242,7 +247,7 @@
|
||||
+ .navds-checkbox__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));
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger));
|
||||
}
|
||||
|
||||
.navds-checkbox--error
|
||||
@@ -250,7 +255,7 @@
|
||||
+ .navds-checkbox__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);
|
||||
box-shadow: 0 0 0 2px var(--ac-radio-checkbox-error-border, var(--a-border-danger)), 0 0 0 4px var(--a-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,12 +292,10 @@
|
||||
.navds-checkbox--readonly
|
||||
> .navds-checkbox__input:hover:not(:checked):not(:indeterminate):not(:focus-visible)
|
||||
+ .navds-checkbox__label::before,
|
||||
.navds-radio--readonly > .navds-radio__input:not(:disabled):not(:checked):not(:indeterminate) + .navds-radio__label::before,
|
||||
.navds-radio--readonly
|
||||
> .navds-radio__input:hover:not(:checked):not(:indeterminate):not(:focus-visible)
|
||||
+ .navds-radio__label::before {
|
||||
.navds-radio--readonly > .navds-radio__input:not(:disabled):not(:checked) + .navds-radio__label::before,
|
||||
.navds-radio--readonly > .navds-radio__input:hover:not(:checked):not(:focus-visible) + .navds-radio__label::before {
|
||||
background-color: var(--__ac-radio-checkbox-readonly-bg);
|
||||
box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border);
|
||||
box-shadow: 0 0 0 2px var(--__ac-radio-checkbox-readonly-border);
|
||||
}
|
||||
|
||||
.navds-checkbox--readonly > .navds-checkbox__input:focus-visible + .navds-checkbox__label::before,
|
||||
@@ -301,8 +304,8 @@
|
||||
}
|
||||
|
||||
.navds-checkbox--readonly > .navds-checkbox__input:checked + .navds-checkbox__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border);
|
||||
background-color: var(--__ac-radio-checkbox-readonly-bg);
|
||||
box-shadow: 0 0 0 2px var(--__ac-radio-checkbox-readonly-border);
|
||||
}
|
||||
|
||||
.navds-checkbox--readonly > .navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
|
||||
@@ -310,12 +313,12 @@
|
||||
}
|
||||
|
||||
.navds-radio--readonly > .navds-radio__input:checked + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border), inset 0 0 0 4px var(--__ac-radio-checkbox-readonly-bg);
|
||||
background-color: var(--a-icon-subtle);
|
||||
box-shadow: 0 0 0 2px var(--__ac-radio-checkbox-readonly-border), inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-bg);
|
||||
}
|
||||
|
||||
.navds-checkbox--readonly > .navds-checkbox__input:indeterminate + .navds-checkbox__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border);
|
||||
box-shadow: 0 0 0 2px var(--__ac-radio-checkbox-readonly-border);
|
||||
background-color: var(--__ac-radio-checkbox-readonly-bg);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
right: var(--a-spacing-2);
|
||||
pointer-events: none;
|
||||
align-self: center;
|
||||
color: var(--ac-select-text, var(--a-text-default));
|
||||
}
|
||||
|
||||
.navds-form-field--small .navds-select__input {
|
||||
@@ -51,7 +52,7 @@
|
||||
}
|
||||
|
||||
.navds-form-field--small .navds-select__chevron {
|
||||
right: 0.375rem;
|
||||
right: var(--a-spacing-1-alt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
}
|
||||
|
||||
.navds-form-field--small .navds-textarea__input {
|
||||
padding: 0.375rem;
|
||||
padding: var(--a-spacing-1-alt);
|
||||
}
|
||||
|
||||
.navds-form-field--small .navds-textarea--counter.navds-textarea__input {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.navds-help-text__button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
@@ -9,17 +8,19 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--ac-help-text-button-color, var(--a-surface-action));
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.25rem;
|
||||
padding: calc(var(--a-spacing-1) / 2);
|
||||
}
|
||||
|
||||
.navds-help-text__button:focus-visible {
|
||||
box-shadow: 0 0 0 1px var(--a-surface-default), 0 0 0 4px var(--a-border-focus);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px var(--a-border-focus), inset 0 0 0 1px var(--a-border-focus);
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
.navds-help-text__button:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px var(--a-surface-default), 0 0 0 4px var(--a-border-focus);
|
||||
box-shadow: 0 0 0 1px var(--a-border-focus), inset 0 0 0 1px var(--a-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "UTF-8";
|
||||
@import "baseline/index.css";
|
||||
@import "typography.css";
|
||||
@import "primitives/index.css";
|
||||
@import "grid.css";
|
||||
@import "accordion.css";
|
||||
@import "alert.css";
|
||||
@@ -34,3 +33,4 @@
|
||||
@import "table.css";
|
||||
@import "tabs.css";
|
||||
@import "list.css";
|
||||
@import "primitives/index.css";
|
||||
@@ -11,6 +11,7 @@
|
||||
position: fixed;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
color: var(--a-text-default);
|
||||
}
|
||||
|
||||
.navds-modal[open] {
|
||||
@@ -22,7 +23,6 @@
|
||||
.navds-modal--polyfilled {
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
overflow: auto;
|
||||
|
||||
/* From polyfill (dialog-polyfill/dist/dialog-polyfill.css): */
|
||||
left: 0;
|
||||
@@ -32,6 +32,10 @@
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.navds-modal--polyfilled .navds-modal--polyfilled {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.navds-modal--polyfilled:not([open]) {
|
||||
display: none; /* from polyfill */
|
||||
}
|
||||
@@ -55,12 +59,17 @@
|
||||
|
||||
@media (min-width: 480px) {
|
||||
.navds-modal {
|
||||
max-width: calc((100% - 6px) - 2em);
|
||||
max-height: calc((100% - 6px) - 2em);
|
||||
max-width: calc(100% - 6px - 2em);
|
||||
}
|
||||
|
||||
.navds-modal--autowidth {
|
||||
max-width: min(700px, calc((100% - 6px) - 2em));
|
||||
max-width: min(700px, calc(100% - 6px - 2em));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-height: 480px) {
|
||||
.navds-modal {
|
||||
max-height: calc(100% - 6px - 2em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +135,14 @@
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* When Datepicker is used nested inside a Modal */
|
||||
.navds-modal--polyfilled .navds-modal--polyfilled.navds-date__nested-modal {
|
||||
min-width: fit-content;
|
||||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@keyframes akselModalFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "5.6.0",
|
||||
"version": "5.10.2",
|
||||
"description": "CSS for NAV Designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
@@ -27,13 +27,13 @@
|
||||
"css:get-version": "node config/get-version.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^5.6.0",
|
||||
"@navikt/ds-tokens": "^5.10.2",
|
||||
"cssnano": "6.0.0",
|
||||
"fast-glob": "3.2.11",
|
||||
"lodash": "4.17.21",
|
||||
"nodemon": "^3.0.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
"postcss": "^8.4.0",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-cli": "^9.0.0",
|
||||
"postcss-combine-duplicated-selectors": "10.0.3",
|
||||
"postcss-import": "^14.1.0"
|
||||
|
||||
@@ -52,8 +52,7 @@
|
||||
}
|
||||
|
||||
.navds-pagination--small .navds-pagination__prev-next:where(.navds-pagination--prev-next--with-text) {
|
||||
padding-left: 0.375rem;
|
||||
padding-right: 0.375rem;
|
||||
padding-inline: var(--a-spacing-1-alt);
|
||||
}
|
||||
|
||||
.navds-pagination--xsmall .navds-pagination__prev-next:where(.navds-pagination--prev-next--with-text) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@import "box.css";
|
||||
@import "bleed.css";
|
||||
@import "page.css";
|
||||
@import "hgrid.css";
|
||||
@import "stack.css";
|
||||
@import "responsive.css";
|
||||
@@ -0,0 +1,50 @@
|
||||
.navds-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
min-height: 100lvh;
|
||||
|
||||
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
|
||||
background-color: var(--__ac-page-background);
|
||||
}
|
||||
|
||||
/* Page.Block */
|
||||
.navds-pageblock {
|
||||
margin-inline: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navds-page__content--grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.navds-page__content--fullheight {
|
||||
min-height: 100vh;
|
||||
min-height: 100lvh;
|
||||
}
|
||||
|
||||
.navds-page__content--padding {
|
||||
padding-block-end: var(--a-spacing-8);
|
||||
}
|
||||
|
||||
.navds-pageblock--lg {
|
||||
max-width: 1024px;
|
||||
}
|
||||
|
||||
.navds-pageblock--xl {
|
||||
max-width: 1280px;
|
||||
}
|
||||
|
||||
.navds-pageblock--2xl {
|
||||
max-width: 1440px;
|
||||
}
|
||||
|
||||
.navds-pageblock--gutters {
|
||||
padding-inline: var(--a-spacing-4);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.navds-pageblock--gutters {
|
||||
padding-inline: var(--a-spacing-12);
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
|
||||
.navds-tabs--small .navds-tabs__scroll-button {
|
||||
padding: 0.375rem var(--a-spacing-4);
|
||||
padding: var(--a-spacing-1-alt) var(--a-spacing-4);
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
.navds-tabs__tab--small {
|
||||
min-height: 2rem;
|
||||
padding: 0.375rem var(--a-spacing-4);
|
||||
padding: var(--a-spacing-1-alt) var(--a-spacing-4);
|
||||
}
|
||||
|
||||
.navds-tabs__tab-icon--top,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
.navds-tag--small {
|
||||
min-height: 1.5rem;
|
||||
padding: 0 0.375rem;
|
||||
padding: 0 var(--a-spacing-1-alt);
|
||||
}
|
||||
|
||||
.navds-tag--xsmall {
|
||||
|
||||
@@ -1,5 +1,213 @@
|
||||
# @navikt/ds-react
|
||||
|
||||
## 5.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- OverridableComponent: Fungerer nå bedre ved bruk av typeof <komponent> ([#2474](https://github.com/navikt/aksel/pull/2474))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.10.2
|
||||
- @navikt/aksel-icons@5.10.2
|
||||
|
||||
## 5.10.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Table: Håndterer nå sjekk for interaktive elementer i ExpandableRow bedre ([#2461](https://github.com/navikt/aksel/pull/2461))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.10.1
|
||||
- @navikt/aksel-icons@5.10.1
|
||||
|
||||
## 5.10.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Primitives: Ny komponent `Page` og `Page.Block` ([#2456](https://github.com/navikt/aksel/pull/2456))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.10.0
|
||||
- @navikt/aksel-icons@5.10.0
|
||||
|
||||
## 5.9.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :bug: Datepicker lukker ikke lenger modalen den er i ([#2451](https://github.com/navikt/aksel/pull/2451))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.9.2
|
||||
- @navikt/aksel-icons@5.9.2
|
||||
|
||||
## 5.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.9.1
|
||||
- @navikt/aksel-icons@5.9.1
|
||||
|
||||
## 5.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Datepicker/Monthpicker: Hvis man bruker komponentene i Modal vil Popover bli erstattet med Modal uansett om man er på desktop eller mobil. ([#2419](https://github.com/navikt/aksel/pull/2419))
|
||||
|
||||
- Datepicker/Monthpicker/Popover: Fjernet `bubbleEscape`-prop. ([#2419](https://github.com/navikt/aksel/pull/2419))
|
||||
|
||||
- useDatepicker/useMonthPicker/useRangedpicker: Fjernet `openOnFocus`-prop, kan nå bare åpnes ved klikk på date-knapp i input. ([#2419](https://github.com/navikt/aksel/pull/2419))
|
||||
|
||||
- Datepicker/Monthpicker: Bytter nå automatisk til Modalvisning på mobil. ([#2419](https://github.com/navikt/aksel/pull/2419))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Added useVirtualFocus hook - used in Combobox for now ([#2394](https://github.com/navikt/aksel/pull/2394))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.9.0
|
||||
- @navikt/aksel-icons@5.9.0
|
||||
|
||||
## 5.8.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- :sparkles: Modal: Støtte for å lukke ved klikk utenfor ([#2386](https://github.com/navikt/aksel/pull/2386))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Autocomplete in combobox will not change formatting of the letters while being typed, but will use the casing of the autocompleted word when selecting the option. ([#2207](https://github.com/navikt/aksel/pull/2207))
|
||||
|
||||
- Accordion: Fikset dom-validering ved bruk av `div` i `button`. ([#2426](https://github.com/navikt/aksel/pull/2426))
|
||||
|
||||
- :bug: Modal: Bedre støtte for Tooltip i Modal ([#2429](https://github.com/navikt/aksel/pull/2429))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.8.0
|
||||
- @navikt/aksel-icons@5.8.0
|
||||
|
||||
## 5.7.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :lipstick: Chips.Toggle: Ikon i uvalgt tilstand ([#2395](https://github.com/navikt/aksel/pull/2395))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.7.6
|
||||
- @navikt/aksel-icons@5.7.6
|
||||
|
||||
## 5.7.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Loader: Har nå riktig `ref`-type. ([#2391](https://github.com/navikt/aksel/pull/2391))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.7.5
|
||||
- @navikt/aksel-icons@5.7.5
|
||||
|
||||
## 5.7.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Datepicker: Fungerer nå bedre i Modal ([#2400](https://github.com/navikt/aksel/pull/2400))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.7.4
|
||||
- @navikt/aksel-icons@5.7.4
|
||||
|
||||
## 5.7.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Forms: Fikset bug der size='small' ikke oppdaterte typografi. ([#2372](https://github.com/navikt/aksel/pull/2372))
|
||||
|
||||
- HelpText: Knapp er visuelt mindre. Klikkflate er fortsatt 24px ([#2376](https://github.com/navikt/aksel/pull/2376))
|
||||
|
||||
- Updated dependencies [[`3d3e90760`](https://github.com/navikt/aksel/commit/3d3e9076088c5e72ed89fb97109b75dcf6e09d96)]:
|
||||
- @navikt/ds-tokens@5.7.3
|
||||
- @navikt/aksel-icons@5.7.3
|
||||
|
||||
## 5.7.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- MonthPicker: Fikset tastaturnavigasjon ([#2374](https://github.com/navikt/aksel/pull/2374))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.7.2
|
||||
- @navikt/aksel-icons@5.7.2
|
||||
|
||||
## 5.7.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Radio: readonly håndterer nå hover-state bedre ([#2363](https://github.com/navikt/aksel/pull/2363))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.7.1
|
||||
- @navikt/aksel-icons@5.7.1
|
||||
|
||||
## 5.7.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Chores: Ryddet opp i sirkulære depdendencies ([#2366](https://github.com/navikt/aksel/pull/2366))
|
||||
|
||||
- :lipstick: CopyButton: Justert padding, gap og animasjon ([#2355](https://github.com/navikt/aksel/pull/2355))
|
||||
|
||||
- Updated dependencies [[`99fca2a56`](https://github.com/navikt/aksel/commit/99fca2a566e7464506f5d64ca1c8f11834cb63ef)]:
|
||||
- @navikt/aksel-icons@5.7.0
|
||||
- @navikt/ds-tokens@5.7.0
|
||||
|
||||
## 5.6.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Datepicker: Popover legger seg nå ikke ovenfor input ([#2349](https://github.com/navikt/aksel/pull/2349))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.6.5
|
||||
- @navikt/aksel-icons@5.6.5
|
||||
|
||||
## 5.6.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- :lipstick: DatePicker: Litt smalere inputfelt ([#2354](https://github.com/navikt/aksel/pull/2354))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.6.4
|
||||
- @navikt/aksel-icons@5.6.4
|
||||
|
||||
## 5.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.6.3
|
||||
- @navikt/aksel-icons@5.6.3
|
||||
|
||||
## 5.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`0f3066d87`](https://github.com/navikt/aksel/commit/0f3066d87aa4adc3ac1b744dd6cab15b5943efd6)]:
|
||||
- @navikt/ds-tokens@5.6.2
|
||||
- @navikt/aksel-icons@5.6.2
|
||||
|
||||
## 5.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Added aria-hidden to AccordionContent when closed, to fix issue where Radio labels were omitted when opening/closing AccordionItems ([#2338](https://github.com/navikt/aksel/pull/2338))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@5.6.1
|
||||
- @navikt/aksel-icons@5.6.1
|
||||
|
||||
## 5.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "5.6.0",
|
||||
"version": "5.10.2",
|
||||
"description": "Aksel react-components for NAV designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -38,8 +38,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "0.25.4",
|
||||
"@navikt/aksel-icons": "^5.6.0",
|
||||
"@navikt/ds-tokens": "^5.6.0",
|
||||
"@navikt/aksel-icons": "^5.10.2",
|
||||
"@navikt/ds-tokens": "^5.10.2",
|
||||
"@radix-ui/react-tabs": "1.0.0",
|
||||
"@radix-ui/react-toggle-group": "1.0.0",
|
||||
"clsx": "^1.2.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { BodyLong } from "../typography/BodyLong";
|
||||
import { BodyLong } from "../typography";
|
||||
import { AccordionItemContext } from "./AccordionItem";
|
||||
|
||||
export interface AccordionContentProps
|
||||
@@ -34,6 +34,9 @@ const AccordionContent = forwardRef<HTMLDivElement, AccordionContentProps>(
|
||||
},
|
||||
className
|
||||
)}
|
||||
aria-hidden={
|
||||
!context.open || undefined
|
||||
} /* Added to fix bug with Radio component, where label text inside a span sometimes is ignored by screen readers after hiding/displaying the RadioGroup inside an Accordion */
|
||||
>
|
||||
{children}
|
||||
</BodyLong>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ChevronDownIcon } from "@navikt/aksel-icons";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { Heading } from "../typography/Heading";
|
||||
import { Heading } from "../typography";
|
||||
import { AccordionContext } from "./AccordionContext";
|
||||
import { AccordionItemContext } from "./AccordionItem";
|
||||
import { ChevronDownIcon } from "@navikt/aksel-icons";
|
||||
|
||||
export interface AccordionHeaderProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
@@ -37,13 +37,13 @@ const AccordionHeader = forwardRef<HTMLButtonElement, AccordionHeaderProps>(
|
||||
aria-expanded={itemContext.open}
|
||||
type="button"
|
||||
>
|
||||
<div className="navds-accordion__icon-wrapper">
|
||||
<span className="navds-accordion__icon-wrapper">
|
||||
<ChevronDownIcon
|
||||
className="navds-accordion__header-chevron"
|
||||
title="Vis mer"
|
||||
aria-hidden
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
<Heading
|
||||
size={accordionContext?.headingSize ?? "small"}
|
||||
as="span"
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "@navikt/aksel-icons";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import { BodyLong } from "../typography/BodyLong";
|
||||
import { BodyLong } from "../typography";
|
||||
import { Button } from "../button";
|
||||
|
||||
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import React, { useRef, useState, forwardRef, useMemo } from "react";
|
||||
import cl from "clsx";
|
||||
import { OverridableComponent, Loader, mergeRefs, Label } from "../";
|
||||
import { omit, useClientLayoutEffect } from "../util";
|
||||
import React, { forwardRef, useMemo, useRef, useState } from "react";
|
||||
import { Loader } from "../loader";
|
||||
import { Label } from "../typography";
|
||||
import {
|
||||
mergeRefs,
|
||||
OverridableComponent,
|
||||
omit,
|
||||
useClientLayoutEffect,
|
||||
} from "../util";
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
|
||||
@@ -43,7 +43,7 @@ const Bubble = forwardRef<HTMLDivElement, BubbleProps>(
|
||||
<div
|
||||
ref={ref}
|
||||
className={cl("navds-chat__bubble", className)}
|
||||
style={{ backgroundColor: backgroundColor }}
|
||||
style={{ backgroundColor }}
|
||||
{...rest}
|
||||
>
|
||||
{(timestamp || name) && (
|
||||
|
||||
@@ -2,7 +2,7 @@ import cl from "clsx";
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import ToggleChips, { ToggleChipsProps } from "./Toggle";
|
||||
import RemovableChips, { RemovableChipsProps } from "./Removable";
|
||||
import { OverridableComponent } from "../util/OverridableComponent";
|
||||
import { OverridableComponent } from "../util";
|
||||
|
||||
export interface ChipsProps extends HTMLAttributes<HTMLUListElement> {
|
||||
children: React.ReactNode;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import { OverridableComponent } from "../util/OverridableComponent";
|
||||
import { OverridableComponent } from "../util";
|
||||
|
||||
export interface ToggleChipsProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: string;
|
||||
/**
|
||||
* Toggles aria-pressed and visual-changes
|
||||
* Toggles aria-pressed and visual changes
|
||||
*/
|
||||
selected?: boolean;
|
||||
/**
|
||||
@@ -44,28 +44,38 @@ export const ToggleChips: OverridableComponent<
|
||||
className={cl(
|
||||
"navds-chips__chip navds-chips__toggle",
|
||||
className,
|
||||
`navds-chips__toggle--${variant}`
|
||||
`navds-chips__toggle--${variant}`,
|
||||
{ "navds-chips__toggle--with-checkmark": checkmark }
|
||||
)}
|
||||
aria-pressed={selected}
|
||||
>
|
||||
{selected && checkmark && (
|
||||
{checkmark && (
|
||||
<svg
|
||||
aria-hidden
|
||||
className="navds-chips__toggle-icon"
|
||||
width="10"
|
||||
height="8"
|
||||
viewBox="0 0 10 8"
|
||||
width="1.25em"
|
||||
height="1.25em"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
focusable={false}
|
||||
role="img"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9.51894 0.45851C9.81799 0.745107 9.8281 1.21987 9.5415 1.51893L3.7915 7.51893C3.51247 7.81009 3.05315 7.82848 2.75174 7.56056L0.501735 5.56056C0.192148 5.28537 0.164263 4.81132 0.439451 4.50173C0.71464 4.19214 1.18869 4.16426 1.49828 4.43944L3.20835 5.95951L8.45852 0.481072C8.74511 0.182015 9.21988 0.171913 9.51894 0.45851Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
{selected ? (
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10 18.125C14.4873 18.125 18.125 14.4873 18.125 10C18.125 5.51269 14.4873 1.875 10 1.875C5.51269 1.875 1.875 5.51269 1.875 10C1.875 14.4873 5.51269 18.125 10 18.125ZM14.128 7.72904C14.3695 7.44357 14.3339 7.01635 14.0485 6.7748C13.763 6.53326 13.3358 6.56886 13.0942 6.85432L8.60428 12.1606L6.41627 9.97263C6.15185 9.70822 5.72315 9.70822 5.45873 9.97263C5.19431 10.2371 5.19431 10.6658 5.45873 10.9302L8.16706 13.6385C8.30095 13.7724 8.48479 13.8441 8.67397 13.8362C8.86316 13.8284 9.0404 13.7416 9.16271 13.5971L14.128 7.72904Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
) : (
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10 3.125C6.20304 3.125 3.125 6.20304 3.125 10C3.125 13.797 6.20304 16.875 10 16.875C13.797 16.875 16.875 13.797 16.875 10C16.875 6.20304 13.797 3.125 10 3.125ZM1.875 10C1.875 5.51269 5.51269 1.875 10 1.875C14.4873 1.875 18.125 5.51269 18.125 10C18.125 14.4873 14.4873 18.125 10 18.125C5.51269 18.125 1.875 14.4873 1.875 10Z"
|
||||
fill="var(--a-border-default)"
|
||||
/>
|
||||
)}
|
||||
</svg>
|
||||
)}
|
||||
<span className="navds-chips__chip-text">{children}</span>
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
import { Meta } from "@storybook/react";
|
||||
import React, { useState } from "react";
|
||||
import { Chips } from ".";
|
||||
|
||||
export default {
|
||||
const meta: Meta<typeof Chips> = {
|
||||
title: "ds-react/Chips",
|
||||
component: Chips,
|
||||
argTypes: {
|
||||
type: {
|
||||
control: {
|
||||
type: "radio",
|
||||
options: ["toggle", "removable"],
|
||||
},
|
||||
},
|
||||
size: {
|
||||
control: {
|
||||
type: "radio",
|
||||
options: ["medium", "small"],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
export default meta;
|
||||
|
||||
const options = ["Norsk", "Dansk", "Svensk", "Tysk", "Spansk"];
|
||||
|
||||
@@ -61,12 +49,22 @@ export const Default = (props) => {
|
||||
</Chips>
|
||||
);
|
||||
};
|
||||
Default.argTypes = {
|
||||
type: {
|
||||
control: { type: "radio" },
|
||||
options: ["toggle", "removable"],
|
||||
},
|
||||
size: {
|
||||
control: { type: "radio" },
|
||||
options: ["medium", "small"],
|
||||
},
|
||||
};
|
||||
|
||||
export const Toggle = () => {
|
||||
export const Toggle = ({ size }) => {
|
||||
const [selected, setSelected] = useState<number[]>([2, 4]);
|
||||
return (
|
||||
<div className="colgap">
|
||||
<Chips>
|
||||
<Chips size={size}>
|
||||
{options.map((c, y) => (
|
||||
<Chips.Toggle
|
||||
selected={selected.includes(y)}
|
||||
@@ -83,7 +81,7 @@ export const Toggle = () => {
|
||||
</Chips.Toggle>
|
||||
))}
|
||||
</Chips>
|
||||
<Chips>
|
||||
<Chips size={size}>
|
||||
{options.map((c, y) => (
|
||||
<Chips.Toggle
|
||||
variant="neutral"
|
||||
@@ -104,6 +102,12 @@ export const Toggle = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Toggle.argTypes = {
|
||||
size: {
|
||||
control: { type: "radio" },
|
||||
options: ["medium", "small"],
|
||||
},
|
||||
};
|
||||
|
||||
export const ToggleNoCheckmark = () => {
|
||||
const [selected, setSelected] = useState<number>(2);
|
||||
|
||||
@@ -8,13 +8,13 @@ import React, {
|
||||
useState,
|
||||
} from "react";
|
||||
import copy from "../util/copy";
|
||||
import Label from "../typography/Label";
|
||||
import { Label } from "../typography";
|
||||
|
||||
export interface CopyButtonProps
|
||||
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
||||
/**
|
||||
* 'xsmall' should only be used in tables
|
||||
* @default "medium"
|
||||
* xsmall should only be used in tables/
|
||||
*/
|
||||
size?: "medium" | "small" | "xsmall";
|
||||
/**
|
||||
@@ -30,12 +30,12 @@ export interface CopyButtonProps
|
||||
*/
|
||||
text?: string;
|
||||
/**
|
||||
* Text shown when button is clicked
|
||||
* Only set if used with 'text'-prop
|
||||
* Text shown when button is clicked.
|
||||
* Only set if used with `text`-prop.
|
||||
*/
|
||||
activeText?: string;
|
||||
/**
|
||||
* Callback when 'copied'-state is active
|
||||
* Callback when 'copied'-state is active
|
||||
*/
|
||||
onActiveChange?: (state: boolean) => void;
|
||||
/**
|
||||
@@ -54,17 +54,17 @@ export interface CopyButtonProps
|
||||
*/
|
||||
activeDuration?: number;
|
||||
/**
|
||||
* * accessible label for icon (ignored if text is set)
|
||||
* Accessible label for icon (ignored if text is set)
|
||||
* @default 'Kopier'
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* accessible label for icon in active-state (ignored if text is set)
|
||||
* Accessible label for icon in active-state (ignored if text is set)
|
||||
* @default 'Kopiert'
|
||||
*/
|
||||
activeTitle?: string;
|
||||
/**
|
||||
* Icon position in Button
|
||||
* Icon position in button
|
||||
* @default "left"
|
||||
*/
|
||||
iconPosition?: "left" | "right";
|
||||
@@ -125,24 +125,23 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
|
||||
}, activeDuration);
|
||||
};
|
||||
|
||||
const CopyIcon = () => {
|
||||
return active ? (
|
||||
<span className="navds-copybutton__icon">
|
||||
{activeIcon ?? (
|
||||
<CheckmarkIcon
|
||||
aria-hidden={!!text}
|
||||
title={text ? undefined : activeTitle}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="navds-copybutton__icon">
|
||||
{icon ?? (
|
||||
<FilesIcon aria-hidden={!!text} title={text ? undefined : title} />
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
const copyIcon = (
|
||||
<span className="navds-copybutton__icon">
|
||||
{active
|
||||
? activeIcon ?? (
|
||||
<CheckmarkIcon
|
||||
aria-hidden={!!text}
|
||||
title={text ? undefined : activeTitle}
|
||||
/>
|
||||
)
|
||||
: icon ?? (
|
||||
<FilesIcon
|
||||
aria-hidden={!!text}
|
||||
title={text ? undefined : title}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -157,14 +156,14 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
|
||||
`navds-copybutton--${variant}`,
|
||||
{
|
||||
"navds-copybutton--icon-only": !text,
|
||||
"navds-copybutton--icon-right": iconPosition === "right",
|
||||
"navds-copybutton--active": active,
|
||||
}
|
||||
)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<span className="navds-copybutton__content">
|
||||
{iconPosition === "left" && <CopyIcon />}
|
||||
|
||||
{iconPosition === "left" && copyIcon}
|
||||
{text &&
|
||||
(active ? (
|
||||
<Label
|
||||
@@ -183,7 +182,7 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
|
||||
{text}
|
||||
</Label>
|
||||
))}
|
||||
{iconPosition === "right" && <CopyIcon />}
|
||||
{iconPosition === "right" && copyIcon}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
import { LinkIcon, ThumbUpIcon } from "@navikt/aksel-icons";
|
||||
import { userEvent, within } from "@storybook/testing-library";
|
||||
import React from "react";
|
||||
import { CopyButton } from ".";
|
||||
import { userEvent, within } from "@storybook/testing-library";
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
import { LinkIcon, ThumbUpIcon } from "@navikt/aksel-icons";
|
||||
import { Tooltip } from "../tooltip";
|
||||
import { CopyButton } from ".";
|
||||
|
||||
export default {
|
||||
const meta: Meta<typeof CopyButton> = {
|
||||
title: "ds-react/CopyButton",
|
||||
component: CopyButton,
|
||||
};
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof CopyButton>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: (props) => <CopyButton {...props} />,
|
||||
argTypes: {
|
||||
size: {
|
||||
defaultValue: "medium",
|
||||
control: { type: "radio" },
|
||||
options: ["small", "medium", "xsmall"],
|
||||
options: ["medium", "small", "xsmall"],
|
||||
},
|
||||
variant: {
|
||||
defaultValue: undefined,
|
||||
control: { type: "radio" },
|
||||
options: ["neutral", "action"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Default = {
|
||||
render: (args) => <CopyButton {...args} />,
|
||||
args: {
|
||||
size: "medium",
|
||||
duration: 2000,
|
||||
activeDuration: 2000,
|
||||
copyText: "3.14",
|
||||
text: "",
|
||||
activeText: "",
|
||||
},
|
||||
};
|
||||
|
||||
export const Interaction = {
|
||||
export const Interaction: Story = {
|
||||
render: () => (
|
||||
<CopyButton
|
||||
copyText="3.14"
|
||||
@@ -50,7 +52,7 @@ export const Interaction = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Variants = {
|
||||
export const Variants: Story = {
|
||||
render: () => (
|
||||
<div className="colgap">
|
||||
<CopyButton copyText="3.14" variant="action" text="Kopier" />
|
||||
@@ -60,7 +62,7 @@ export const Variants = {
|
||||
),
|
||||
};
|
||||
|
||||
export const IconPosition = {
|
||||
export const IconPosition: Story = {
|
||||
render: () => (
|
||||
<div className="colgap">
|
||||
<CopyButton copyText="3.14" iconPosition="left" text="Kopier" />
|
||||
@@ -70,7 +72,7 @@ export const IconPosition = {
|
||||
),
|
||||
};
|
||||
|
||||
export const Sizes = {
|
||||
export const Sizes: Story = {
|
||||
render: () => (
|
||||
<div className="colgap">
|
||||
<div className="rowgap">
|
||||
@@ -115,7 +117,7 @@ export const Sizes = {
|
||||
),
|
||||
};
|
||||
|
||||
export const Texts = {
|
||||
export const Texts: Story = {
|
||||
render: () => (
|
||||
<div className="colgap">
|
||||
<div>
|
||||
@@ -145,7 +147,7 @@ export const Texts = {
|
||||
),
|
||||
};
|
||||
|
||||
export const Icons = {
|
||||
export const Icons: Story = {
|
||||
render: () => (
|
||||
<div className="rowgap">
|
||||
<div>
|
||||
@@ -175,7 +177,7 @@ export const Icons = {
|
||||
),
|
||||
};
|
||||
|
||||
export const InlineDemo = {
|
||||
export const InlineDemo: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: "flex", gap: "0.5rem", alignItems: "center" }}>
|
||||
<CopyButton size="small" copyText="3.14" /> Kopier dette feltet
|
||||
@@ -183,7 +185,7 @@ export const InlineDemo = {
|
||||
),
|
||||
};
|
||||
|
||||
export const WithTooltip = {
|
||||
export const WithTooltip: Story = {
|
||||
render: () => {
|
||||
return (
|
||||
<div>
|
||||
@@ -195,11 +197,11 @@ export const WithTooltip = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Duration = {
|
||||
export const Duration: Story = {
|
||||
render: () => <CopyButton copyText="3.14" activeDuration={300} />,
|
||||
};
|
||||
|
||||
export const Disabled = {
|
||||
export const Disabled: Story = {
|
||||
render: () => (
|
||||
<div className="colgap">
|
||||
<CopyButton copyText="3.14" disabled />
|
||||
|
||||
@@ -13,13 +13,13 @@ interface DateContextContextProps {
|
||||
* Aria-connected ID
|
||||
*/
|
||||
ariaId?: string;
|
||||
/**
|
||||
* Flag for enabled-check
|
||||
*/
|
||||
defined: boolean;
|
||||
}
|
||||
|
||||
export const DateContext = createContext<DateContextContextProps>({
|
||||
open: false,
|
||||
onOpen: () => null,
|
||||
ariaId: undefined,
|
||||
});
|
||||
export const DateContext = createContext<DateContextContextProps | null>(null);
|
||||
|
||||
export const useDateInputContext = () => {
|
||||
const context = useContext(DateContext);
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
import cl from "clsx";
|
||||
import isWeekend from "date-fns/isWeekend";
|
||||
import React, { forwardRef, useRef, useState } from "react";
|
||||
import {
|
||||
DateRange,
|
||||
DayPicker,
|
||||
DayPickerBase,
|
||||
Matcher,
|
||||
isMatch,
|
||||
} from "react-day-picker";
|
||||
import { Popover, omit, useId } from "../..";
|
||||
import { DateInputProps, DatePickerInput } from "../DateInput";
|
||||
import React, { forwardRef, useMemo, useRef, useState } from "react";
|
||||
import { DateRange, DayPicker, isMatch } from "react-day-picker";
|
||||
import { mergeRefs, omit, useId } from "../../util";
|
||||
import { DateContext } from "../context";
|
||||
import { DatePickerInput } from "../parts/DateInput";
|
||||
import { DateWrapper } from "../parts/DateWrapper";
|
||||
import { getLocaleFromString, labels } from "../utils";
|
||||
import DatePickerStandalone, {
|
||||
DatePickerStandaloneType,
|
||||
} from "./DatePickerStandalone";
|
||||
import DatePickerStandalone from "./DatePickerStandalone";
|
||||
import Caption from "./parts/Caption";
|
||||
import DayButton from "./parts/DayButton";
|
||||
import DropdownCaption from "./parts/DropdownCaption";
|
||||
@@ -22,114 +15,7 @@ import { HeadRow } from "./parts/HeadRow";
|
||||
import Row from "./parts/Row";
|
||||
import TableHead from "./parts/TableHead";
|
||||
import WeekNumber from "./parts/WeekNumber";
|
||||
|
||||
export type SingleMode = {
|
||||
mode?: "single";
|
||||
onSelect?: (val?: Date) => void;
|
||||
selected?: Date;
|
||||
defaultSelected?: Date;
|
||||
onWeekNumberClick?: never;
|
||||
};
|
||||
|
||||
export type MultipleMode = {
|
||||
mode: "multiple";
|
||||
onSelect?: (val?: Date[]) => void;
|
||||
selected?: Date[];
|
||||
defaultSelected?: Date[];
|
||||
min?: number;
|
||||
max?: number;
|
||||
/**
|
||||
* Allows selecting a week at a time. Only used with mode="multiple".
|
||||
*/
|
||||
onWeekNumberClick?: DayPickerBase["onWeekNumberClick"];
|
||||
};
|
||||
|
||||
export type RangeMode = {
|
||||
mode: "range";
|
||||
onSelect?: (val?: DateRange) => void;
|
||||
selected?: DateRange;
|
||||
defaultSelected?: DateRange;
|
||||
min?: number;
|
||||
max?: number;
|
||||
onWeekNumberClick?: never;
|
||||
};
|
||||
|
||||
type ConditionalModeProps = SingleMode | MultipleMode | RangeMode;
|
||||
|
||||
//github.com/gpbl/react-day-picker/blob/50b6dba/packages/react-day-picker/src/types/DayPickerBase.ts#L139
|
||||
export interface DatePickerDefaultProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect">,
|
||||
Pick<DayPickerBase, "month" | "onMonthChange" | "today" | "onDayClick"> {
|
||||
/**
|
||||
* Element datepicker anchors to. Use <DatePicker.Input /> for built-in toggle, or make your own with the open/onClose props
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Classname for datepicker in popover
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Classname for wrapper
|
||||
*/
|
||||
wrapperClassName?: string;
|
||||
/**
|
||||
* Changes datepicker locale
|
||||
* @default "nb" (norsk bokmål)
|
||||
*/
|
||||
locale?: "nb" | "nn" | "en";
|
||||
/**
|
||||
* The earliest day to start navigation.
|
||||
*/
|
||||
fromDate?: Date;
|
||||
/**
|
||||
* The latests day to end navigation.
|
||||
*/
|
||||
toDate?: Date;
|
||||
/**
|
||||
* Display dropdown for choosing the month and the year. Needs `fromDate` + `toDate` to work.
|
||||
* @default false
|
||||
*/
|
||||
dropdownCaption?: boolean;
|
||||
/**
|
||||
* Apply the disabled modifier to the matching days.
|
||||
* https://react-day-picker.js.org/api/types/Matcher
|
||||
*/
|
||||
disabled?: Matcher[];
|
||||
/**
|
||||
* Disable saturday and sunday.
|
||||
* @default false
|
||||
*/
|
||||
disableWeekends?: boolean;
|
||||
/**
|
||||
* Shows week numbers in left-column. Use with caution, takes up valuable screenspace for small screens.
|
||||
* @default false
|
||||
*/
|
||||
showWeekNumber?: boolean;
|
||||
/**
|
||||
* Open state for user-controlled state. Component controlled by default
|
||||
*/
|
||||
open?: boolean;
|
||||
/**
|
||||
* onClose callback for user-controlled state
|
||||
*/
|
||||
onClose?: () => void;
|
||||
/**
|
||||
* onOpenToggle callback for user-controlled state. Only called if `<DatePicker.Input />` is used
|
||||
*/
|
||||
onOpenToggle?: () => void;
|
||||
/**
|
||||
* Avoid using if possible!
|
||||
* Changes what CSS position property to use.
|
||||
* You want to use "fixed" if parent wrapper has position relative, but you want popover to escape
|
||||
* @default See Popover
|
||||
*/
|
||||
strategy?: "absolute" | "fixed";
|
||||
/**
|
||||
* Bubbles Escape keydown-event up trough DOM-tree. This is set to false by default to prevent closing components like Modal on Escape
|
||||
* @default false
|
||||
*/
|
||||
bubbleEscape?: boolean;
|
||||
}
|
||||
import { ConditionalModeProps, DatePickerDefaultProps } from "./types";
|
||||
|
||||
export type DatePickerProps = DatePickerDefaultProps & ConditionalModeProps;
|
||||
|
||||
@@ -145,14 +31,12 @@ interface DatePickerComponent
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
Standalone: DatePickerStandaloneType;
|
||||
Standalone: typeof DatePickerStandalone;
|
||||
/**
|
||||
* Custom TextField for DatePicker
|
||||
* @see 🏷️ {@link DateInputProps}
|
||||
*/
|
||||
Input: React.ForwardRefExoticComponent<
|
||||
DateInputProps & React.RefAttributes<HTMLInputElement>
|
||||
>;
|
||||
Input: typeof DatePickerInput;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,7 +79,6 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
||||
onClose,
|
||||
onOpenToggle,
|
||||
strategy,
|
||||
bubbleEscape = false,
|
||||
onWeekNumberClick,
|
||||
...rest
|
||||
},
|
||||
@@ -205,6 +88,7 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
||||
const [open, setOpen] = useState(_open ?? false);
|
||||
|
||||
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
const mergedRef = useMemo(() => mergeRefs([wrapperRef, ref]), [ref]);
|
||||
|
||||
const [selectedDates, setSelectedDates] = React.useState<
|
||||
Date | Date[] | DateRange | undefined
|
||||
@@ -213,19 +97,57 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
||||
const mode = rest.mode ?? ("single" as any);
|
||||
|
||||
/**
|
||||
* @param selected Date | Date[] | DateRange | undefined
|
||||
* @param newSelected Date | Date[] | DateRange | undefined
|
||||
*/
|
||||
const handleSelect = (selected) => {
|
||||
setSelectedDates(selected);
|
||||
const handleSelect = (newSelected) => {
|
||||
setSelectedDates(newSelected);
|
||||
|
||||
if (rest.mode === "single") {
|
||||
selected && (onClose?.() ?? setOpen(false));
|
||||
newSelected && (onClose?.() ?? setOpen(false));
|
||||
} else if (rest.mode === "range") {
|
||||
selected?.from && selected?.to && (onClose?.() ?? setOpen(false));
|
||||
newSelected?.from && newSelected?.to && (onClose?.() ?? setOpen(false));
|
||||
}
|
||||
rest?.onSelect?.(selected);
|
||||
rest?.onSelect?.(newSelected);
|
||||
};
|
||||
|
||||
const DatePickerComponent = (
|
||||
<DayPicker
|
||||
locale={getLocaleFromString(locale)}
|
||||
mode={mode}
|
||||
onSelect={handleSelect}
|
||||
selected={selected ?? selectedDates}
|
||||
components={{
|
||||
Caption: dropdownCaption ? DropdownCaption : Caption,
|
||||
Head: TableHead,
|
||||
HeadRow,
|
||||
WeekNumber,
|
||||
Row,
|
||||
Day: DayButton,
|
||||
}}
|
||||
className={cl("navds-date", className)}
|
||||
classNames={{
|
||||
vhidden: "navds-sr-only",
|
||||
}}
|
||||
disabled={(day) => {
|
||||
return (disableWeekends && isWeekend(day)) || isMatch(day, disabled);
|
||||
}}
|
||||
weekStartsOn={1}
|
||||
initialFocus={false}
|
||||
labels={labels as any}
|
||||
modifiers={{
|
||||
weekend: (day) => disableWeekends && isWeekend(day),
|
||||
}}
|
||||
modifiersClassNames={{
|
||||
weekend: "rdp-day__weekend",
|
||||
}}
|
||||
showWeekNumber={showWeekNumber}
|
||||
onWeekNumberClick={mode === "multiple" ? onWeekNumberClick : undefined}
|
||||
fixedWeeks
|
||||
showOutsideDays
|
||||
{...omit(rest, ["onSelect"])}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<DateContext.Provider
|
||||
value={{
|
||||
@@ -235,71 +157,27 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
||||
onOpenToggle?.();
|
||||
},
|
||||
ariaId,
|
||||
defined: true,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
ref={mergedRef}
|
||||
className={cl("navds-date__wrapper", wrapperClassName)}
|
||||
>
|
||||
{children}
|
||||
{(_open ?? open) && (
|
||||
<Popover
|
||||
arrow={false}
|
||||
anchorEl={wrapperRef.current}
|
||||
open={_open ?? open}
|
||||
onClose={() => {
|
||||
onClose?.() ?? setOpen(false);
|
||||
}}
|
||||
placement="bottom-start"
|
||||
id={ariaId}
|
||||
role="dialog"
|
||||
ref={ref}
|
||||
strategy={strategy}
|
||||
className="navds-date__popover"
|
||||
bubbleEscape={bubbleEscape}
|
||||
>
|
||||
<DayPicker
|
||||
locale={getLocaleFromString(locale)}
|
||||
mode={mode}
|
||||
onSelect={handleSelect}
|
||||
selected={selected ?? selectedDates}
|
||||
components={{
|
||||
Caption: dropdownCaption ? DropdownCaption : Caption,
|
||||
Head: TableHead,
|
||||
HeadRow,
|
||||
WeekNumber,
|
||||
Row,
|
||||
Day: DayButton,
|
||||
}}
|
||||
className={cl("navds-date", className)}
|
||||
classNames={{
|
||||
vhidden: "navds-sr-only",
|
||||
}}
|
||||
disabled={(day) => {
|
||||
return (
|
||||
(disableWeekends && isWeekend(day)) ||
|
||||
isMatch(day, disabled)
|
||||
);
|
||||
}}
|
||||
weekStartsOn={1}
|
||||
initialFocus={false}
|
||||
labels={labels as any}
|
||||
modifiers={{
|
||||
weekend: (day) => disableWeekends && isWeekend(day),
|
||||
}}
|
||||
modifiersClassNames={{
|
||||
weekend: "rdp-day__weekend",
|
||||
}}
|
||||
showWeekNumber={showWeekNumber}
|
||||
onWeekNumberClick={
|
||||
mode === "multiple" ? onWeekNumberClick : undefined
|
||||
}
|
||||
fixedWeeks
|
||||
showOutsideDays
|
||||
{...omit(rest, ["onSelect"])}
|
||||
/>
|
||||
</Popover>
|
||||
)}
|
||||
<DateWrapper
|
||||
open={_open ?? open}
|
||||
anchor={wrapperRef.current}
|
||||
onClose={() => onClose?.() ?? setOpen(false)}
|
||||
locale={locale}
|
||||
variant={mode}
|
||||
popoverProps={{
|
||||
id: ariaId,
|
||||
strategy,
|
||||
}}
|
||||
>
|
||||
{DatePickerComponent}
|
||||
</DateWrapper>
|
||||
</div>
|
||||
</DateContext.Provider>
|
||||
);
|
||||
|
||||
@@ -2,21 +2,21 @@ import cl from "clsx";
|
||||
import isWeekend from "date-fns/isWeekend";
|
||||
import React, { forwardRef } from "react";
|
||||
import { DateRange, DayPicker, isMatch } from "react-day-picker";
|
||||
import { omit } from "../..";
|
||||
import { omit } from "../../util";
|
||||
import { getLocaleFromString, labels } from "../utils";
|
||||
import Caption from "./parts/Caption";
|
||||
import DayButton from "./parts/DayButton";
|
||||
import DropdownCaption from "./parts/DropdownCaption";
|
||||
import { HeadRow } from "./parts/HeadRow";
|
||||
import Row from "./parts/Row";
|
||||
import TableHead from "./parts/TableHead";
|
||||
import WeekNumber from "./parts/WeekNumber";
|
||||
import {
|
||||
DatePickerDefaultProps,
|
||||
MultipleMode,
|
||||
RangeMode,
|
||||
SingleMode,
|
||||
} from "./DatePicker";
|
||||
import TableHead from "./parts/TableHead";
|
||||
import WeekNumber from "./parts/WeekNumber";
|
||||
import Caption from "./parts/Caption";
|
||||
import DropdownCaption from "./parts/DropdownCaption";
|
||||
import Row from "./parts/Row";
|
||||
import { HeadRow } from "./parts/HeadRow";
|
||||
import DayButton from "./parts/DayButton";
|
||||
} from "./types";
|
||||
|
||||
interface DatePickerStandaloneDefaultProps
|
||||
extends Omit<
|
||||
@@ -71,11 +71,11 @@ export const DatePickerStandalone: DatePickerStandaloneType = forwardRef<
|
||||
const mode = rest.mode ?? ("single" as any);
|
||||
|
||||
/**
|
||||
* @param selected Date | Date[] | DateRange | undefined
|
||||
* @param newSelected Date | Date[] | DateRange | undefined
|
||||
*/
|
||||
const handleSelect = (selected) => {
|
||||
setSelectedDates(selected);
|
||||
onSelect?.(selected);
|
||||
const handleSelect = (newSelected) => {
|
||||
setSelectedDates(newSelected);
|
||||
onSelect?.(newSelected);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
import isSameDay from "date-fns/isSameDay";
|
||||
import React, { useId, useState } from "react";
|
||||
import { useDatepicker, useRangeDatepicker } from "..";
|
||||
import { Button, HGrid, VStack } from "../..";
|
||||
import { BodyLong, Button, HGrid, Modal, VStack } from "../..";
|
||||
import DatePicker, { DatePickerProps } from "./DatePicker";
|
||||
import isSameDay from "date-fns/isSameDay";
|
||||
|
||||
const disabledDays = [
|
||||
new Date("Oct 10 2022"),
|
||||
@@ -18,7 +18,6 @@ export default {
|
||||
|
||||
type DefaultStoryProps = DatePickerProps & {
|
||||
size: "medium" | "small";
|
||||
openOnFocus: boolean;
|
||||
inputfield: boolean;
|
||||
standalone: boolean;
|
||||
};
|
||||
@@ -30,13 +29,11 @@ export const Default: StoryObj<DefaultStoryProps> = {
|
||||
const rangeCtx = useRangeDatepicker({
|
||||
fromDate: new Date("Aug 23 2020"),
|
||||
toDate: new Date("Aug 23 2023"),
|
||||
openOnFocus: props.openOnFocus,
|
||||
});
|
||||
|
||||
const singleCtx = useDatepicker({
|
||||
fromDate: new Date("Aug 23 2020"),
|
||||
toDate: new Date("Aug 23 2023"),
|
||||
openOnFocus: props.openOnFocus,
|
||||
});
|
||||
|
||||
const newProps = {
|
||||
@@ -111,7 +108,6 @@ export const Default: StoryObj<DefaultStoryProps> = {
|
||||
disableWeekends: false,
|
||||
showWeekNumber: false,
|
||||
mode: "single",
|
||||
openOnFocus: true,
|
||||
inputfield: true,
|
||||
standalone: false,
|
||||
},
|
||||
@@ -186,21 +182,6 @@ export const UseRangedDatepicker = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const OpenOnFocus = () => {
|
||||
const { datepickerProps, inputProps } = useDatepicker({
|
||||
onDateChange: console.log,
|
||||
openOnFocus: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", gap: "1rem" }}>
|
||||
<DatePicker {...datepickerProps}>
|
||||
<DatePicker.Input {...inputProps} label="Velg dato" />
|
||||
</DatePicker>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const NB = () => (
|
||||
<DatePicker.Standalone locale="nb" today={new Date("2006-07-01")} />
|
||||
);
|
||||
@@ -333,15 +314,32 @@ export const Size = () => {
|
||||
fromDate: new Date("Aug 23 2019"),
|
||||
toDate: new Date("Feb 23 2024"),
|
||||
onDateChange: console.log,
|
||||
defaultSelected: new Date("Feb 23 2023"),
|
||||
});
|
||||
const { datepickerProps: d2, inputProps: i2 } = useDatepicker({
|
||||
fromDate: new Date("Aug 23 2019"),
|
||||
toDate: new Date("Feb 23 2024"),
|
||||
onDateChange: console.log,
|
||||
defaultSelected: new Date("Feb 23 2023"),
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", gap: "1rem" }}>
|
||||
<DatePicker {...datepickerProps} dropdownCaption>
|
||||
<DatePicker.Input size="medium" {...inputProps} label="Velg dato" />
|
||||
<DatePicker.Input
|
||||
placeholder="10/10/2023"
|
||||
size="medium"
|
||||
{...inputProps}
|
||||
label="Velg dato"
|
||||
/>
|
||||
</DatePicker>
|
||||
<DatePicker {...datepickerProps} dropdownCaption>
|
||||
<DatePicker.Input size="small" {...inputProps} label="Velg dato" />
|
||||
<DatePicker {...d2} dropdownCaption>
|
||||
<DatePicker.Input
|
||||
placeholder="10/10/2023"
|
||||
size="small"
|
||||
{...i2}
|
||||
label="Velg dato"
|
||||
/>
|
||||
</DatePicker>
|
||||
</div>
|
||||
);
|
||||
@@ -432,3 +430,43 @@ export const WeekDayClick = () => {
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
export const ModalDemo = () => {
|
||||
const { datepickerProps, inputProps } = useDatepicker({
|
||||
fromDate: new Date("Aug 23 2019"),
|
||||
toDate: new Date("Feb 23 2024"),
|
||||
onDateChange: console.log,
|
||||
});
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(!open)}>Open modal</Button>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
header={{ heading: "Modal-demo" }}
|
||||
>
|
||||
<Modal.Body style={{ position: "relative" }}>
|
||||
<BodyLong spacing>
|
||||
Lorem ipsum dolor sit, amet consectetur adipisicing elit.
|
||||
</BodyLong>
|
||||
|
||||
<DatePicker {...datepickerProps} dropdownCaption>
|
||||
<DatePicker.Input
|
||||
{...inputProps}
|
||||
label="Velg dato"
|
||||
description="Format: dd.mm.yyyy"
|
||||
/>
|
||||
</DatePicker>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button>Neste</Button>
|
||||
<Button variant="secondary">Tilbake</Button>
|
||||
<Button variant="tertiary">Avbryt</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
ModalDemo.parameters = { chromatic: { pauseAnimationAtEnd: true } };
|
||||
@@ -1,8 +1,10 @@
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "@navikt/aksel-icons";
|
||||
import React from "react";
|
||||
import { CaptionProps, useDayPicker, useNavigation } from "react-day-picker";
|
||||
import { Button, Label } from "../../..";
|
||||
|
||||
import WeekRow from "./WeekRow";
|
||||
import { Button } from "../../../button";
|
||||
import { Label } from "../../../typography";
|
||||
|
||||
/**
|
||||
* https://github.com/gpbl/react-day-picker/tree/main/src/components/Caption
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "@navikt/aksel-icons";
|
||||
import { max, min } from "date-fns";
|
||||
import setMonth from "date-fns/setMonth";
|
||||
import setYear from "date-fns/setYear";
|
||||
import startOfMonth from "date-fns/startOfMonth";
|
||||
import React from "react";
|
||||
import { CaptionProps, useDayPicker, useNavigation } from "react-day-picker";
|
||||
import { Button, Select } from "../../..";
|
||||
import { Button } from "../../../button";
|
||||
import { Select } from "../../../form/Select";
|
||||
import { getMonths, getYears } from "../../utils/get-dates";
|
||||
import { labelMonthDropdown, labelYearDropdown } from "../../utils/labels";
|
||||
import { max, min } from "date-fns";
|
||||
import WeekRow from "./WeekRow";
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,10 +2,10 @@ import React from "react";
|
||||
import { useDayPicker } from "react-day-picker";
|
||||
import { Show } from "../../../layout/responsive";
|
||||
import { Detail } from "../../../typography";
|
||||
import { useId } from "../../../util";
|
||||
import { getMonthWeeks } from "../../utils/get-month-weeks";
|
||||
import { labelWeek } from "../../utils/labels";
|
||||
import WeekNumber from "./WeekNumber";
|
||||
import { useId } from "../../../util";
|
||||
|
||||
const WeekRow = ({ displayMonth }: { displayMonth: Date }) => {
|
||||
const { locale, fixedWeeks, onWeekNumberClick } = useDayPicker();
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { DateRange, DayPickerBase, Matcher } from "react-day-picker";
|
||||
|
||||
export type SingleMode = {
|
||||
mode?: "single";
|
||||
onSelect?: (val?: Date) => void;
|
||||
selected?: Date;
|
||||
defaultSelected?: Date;
|
||||
onWeekNumberClick?: never;
|
||||
};
|
||||
|
||||
export type MultipleMode = {
|
||||
mode: "multiple";
|
||||
onSelect?: (val?: Date[]) => void;
|
||||
selected?: Date[];
|
||||
defaultSelected?: Date[];
|
||||
min?: number;
|
||||
max?: number;
|
||||
/**
|
||||
* Allows selecting a week at a time. Only used with mode="multiple".
|
||||
*/
|
||||
onWeekNumberClick?: DayPickerBase["onWeekNumberClick"];
|
||||
};
|
||||
|
||||
export type RangeMode = {
|
||||
mode: "range";
|
||||
onSelect?: (val?: DateRange) => void;
|
||||
selected?: DateRange;
|
||||
defaultSelected?: DateRange;
|
||||
min?: number;
|
||||
max?: number;
|
||||
onWeekNumberClick?: never;
|
||||
};
|
||||
|
||||
export type ConditionalModeProps = SingleMode | MultipleMode | RangeMode;
|
||||
|
||||
//github.com/gpbl/react-day-picker/blob/50b6dba/packages/react-day-picker/src/types/DayPickerBase.ts#L139
|
||||
export interface DatePickerDefaultProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect">,
|
||||
Pick<DayPickerBase, "month" | "onMonthChange" | "today" | "onDayClick"> {
|
||||
/**
|
||||
* Element datepicker anchors to. Use <DatePicker.Input /> for built-in toggle, or make your own with the open/onClose props
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Classname for datepicker in popover
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Classname for wrapper
|
||||
*/
|
||||
wrapperClassName?: string;
|
||||
/**
|
||||
* Changes datepicker locale
|
||||
* @default "nb" (norsk bokmål)
|
||||
*/
|
||||
locale?: "nb" | "nn" | "en";
|
||||
/**
|
||||
* The earliest day to start navigation.
|
||||
*/
|
||||
fromDate?: Date;
|
||||
/**
|
||||
* The latests day to end navigation.
|
||||
*/
|
||||
toDate?: Date;
|
||||
/**
|
||||
* Display dropdown for choosing the month and the year. Needs `fromDate` + `toDate` to work.
|
||||
* @default false
|
||||
*/
|
||||
dropdownCaption?: boolean;
|
||||
/**
|
||||
* Apply the disabled modifier to the matching days.
|
||||
* https://react-day-picker.js.org/api/types/Matcher
|
||||
*/
|
||||
disabled?: Matcher[];
|
||||
/**
|
||||
* Disable saturday and sunday.
|
||||
* @default false
|
||||
*/
|
||||
disableWeekends?: boolean;
|
||||
/**
|
||||
* Shows week numbers in left-column. Use with caution, takes up valuable screenspace for small screens.
|
||||
* @default false
|
||||
*/
|
||||
showWeekNumber?: boolean;
|
||||
/**
|
||||
* Open state for user-controlled state. Component controlled by default
|
||||
*/
|
||||
open?: boolean;
|
||||
/**
|
||||
* onClose callback for user-controlled state
|
||||
*/
|
||||
onClose?: () => void;
|
||||
/**
|
||||
* onOpenToggle callback for user-controlled state. Only called if `<DatePicker.Input />` is used
|
||||
*/
|
||||
onOpenToggle?: () => void;
|
||||
/**
|
||||
* Avoid using if possible!
|
||||
* Changes what CSS position property to use.
|
||||
* You want to use "fixed" if parent wrapper has position relative, but you want popover to escape
|
||||
* @default See Popover
|
||||
*/
|
||||
strategy?: "absolute" | "fixed";
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
import differenceInCalendarDays from "date-fns/differenceInCalendarDays";
|
||||
import isWeekend from "date-fns/isWeekend";
|
||||
import React, { useCallback, useRef, useState } from "react";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { DayClickEventHandler, isMatch } from "react-day-picker";
|
||||
import { DateInputProps } from "../DateInput";
|
||||
import { DatePickerProps } from "../datepicker/DatePicker";
|
||||
import { DateInputProps } from "../parts/DateInput";
|
||||
import {
|
||||
formatDateForInput,
|
||||
getLocaleFromString,
|
||||
isValidDate,
|
||||
parseDate,
|
||||
} from "../utils";
|
||||
import { useEscape } from "./useEscape";
|
||||
import { useOutsideClickHandler } from "./useOutsideClickHandler";
|
||||
|
||||
export interface UseDatepickerOptions
|
||||
extends Pick<
|
||||
@@ -59,10 +57,10 @@ export interface UseDatepickerOptions
|
||||
*/
|
||||
allowTwoDigitYear?: boolean;
|
||||
/**
|
||||
* Opens datepicker on input-focus
|
||||
* @default true
|
||||
* Will be removed in a future major-version
|
||||
* @deprecated
|
||||
*/
|
||||
openOnFocus?: boolean;
|
||||
openOnFocus?: false;
|
||||
}
|
||||
|
||||
interface UseDatepickerValue {
|
||||
@@ -76,7 +74,14 @@ interface UseDatepickerValue {
|
||||
inputProps: Pick<
|
||||
DateInputProps,
|
||||
"onChange" | "onFocus" | "onBlur" | "value"
|
||||
> & { ref: React.RefObject<HTMLInputElement> };
|
||||
> & {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setAnchorRef: React.Dispatch<
|
||||
React.SetStateAction<HTMLButtonElement | null>
|
||||
>;
|
||||
};
|
||||
/**
|
||||
* Resets all states (callback)
|
||||
*/
|
||||
@@ -143,14 +148,11 @@ export const useDatepicker = (
|
||||
onValidate,
|
||||
defaultMonth,
|
||||
allowTwoDigitYear = true,
|
||||
openOnFocus = true,
|
||||
} = opt;
|
||||
|
||||
const [anchorRef, setAnchorRef] = useState<HTMLButtonElement | null>(null);
|
||||
const locale = getLocaleFromString(_locale);
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [daypickerRef, setDaypickerRef] = useState<HTMLDivElement>();
|
||||
|
||||
const [defaultSelected, setDefaultSelected] = useState(_defaultSelected);
|
||||
|
||||
// Initialize states
|
||||
@@ -164,21 +166,14 @@ export const useDatepicker = (
|
||||
const [inputValue, setInputValue] = useState(defaultInputValue);
|
||||
|
||||
const handleOpen = useCallback(
|
||||
(open: boolean) => {
|
||||
setOpen(open);
|
||||
open && setMonth(selectedDay ?? defaultSelected ?? defaultMonth ?? today);
|
||||
(newOpen: boolean) => {
|
||||
setOpen(newOpen);
|
||||
newOpen &&
|
||||
setMonth(selectedDay ?? defaultSelected ?? defaultMonth ?? today);
|
||||
},
|
||||
[defaultMonth, defaultSelected, selectedDay, today]
|
||||
);
|
||||
|
||||
useOutsideClickHandler(open, handleOpen, [
|
||||
daypickerRef,
|
||||
inputRef.current,
|
||||
inputRef.current?.nextSibling,
|
||||
]);
|
||||
|
||||
useEscape(open, handleOpen, inputRef);
|
||||
|
||||
const updateDate = (date?: Date) => {
|
||||
onDateChange?.(date);
|
||||
setSelectedDay(date);
|
||||
@@ -206,7 +201,6 @@ export const useDatepicker = (
|
||||
if (e.target.readOnly) {
|
||||
return;
|
||||
}
|
||||
!open && openOnFocus && handleOpen(true);
|
||||
const day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
@@ -242,7 +236,7 @@ export const useDatepicker = (
|
||||
const handleDayClick: DayClickEventHandler = (day, { selected }) => {
|
||||
if (day && !selected) {
|
||||
handleOpen(false);
|
||||
inputRef.current && inputRef.current.focus();
|
||||
anchorRef?.focus();
|
||||
}
|
||||
|
||||
if (!required && selected) {
|
||||
@@ -310,7 +304,7 @@ export const useDatepicker = (
|
||||
|
||||
const datepickerProps = {
|
||||
month,
|
||||
onMonthChange: (month) => setMonth(month),
|
||||
onMonthChange: setMonth,
|
||||
onDayClick: handleDayClick,
|
||||
selected: selectedDay ?? new Date("Invalid date"),
|
||||
locale: _locale,
|
||||
@@ -318,11 +312,13 @@ export const useDatepicker = (
|
||||
toDate,
|
||||
today,
|
||||
open,
|
||||
onClose: () => {
|
||||
handleOpen(false);
|
||||
anchorRef?.focus();
|
||||
},
|
||||
onOpenToggle: () => handleOpen(!open),
|
||||
disabled,
|
||||
disableWeekends,
|
||||
bubbleEscape: true,
|
||||
ref: setDaypickerRef,
|
||||
};
|
||||
|
||||
const inputProps = {
|
||||
@@ -330,7 +326,7 @@ export const useDatepicker = (
|
||||
onFocus: handleFocus,
|
||||
onBlur: handleBlur,
|
||||
value: inputValue,
|
||||
ref: inputRef,
|
||||
setAnchorRef,
|
||||
};
|
||||
|
||||
return { datepickerProps, inputProps, reset, selectedDay, setSelected };
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { useCallback, useEffect, RefObject } from "react";
|
||||
|
||||
export const useEscape = (
|
||||
open: boolean,
|
||||
setOpen: (openState: boolean) => void,
|
||||
focusRef: RefObject<HTMLElement>
|
||||
) => {
|
||||
const handleClose = useCallback(() => {
|
||||
setOpen(false);
|
||||
focusRef?.current && focusRef.current.focus();
|
||||
}, [focusRef, setOpen]);
|
||||
|
||||
const escape = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
if (open && event.key === "Escape") {
|
||||
event.preventDefault(); // This prevents modal from closing when using datepicker inside modal
|
||||
handleClose();
|
||||
}
|
||||
},
|
||||
[handleClose, open]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", escape, false);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", escape, false);
|
||||
};
|
||||
}, [escape]);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { DateInputProps } from "../DateInput";
|
||||
import { MonthPickerProps } from "../monthpicker/MonthPicker";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { MonthPickerProps } from "../monthpicker/types";
|
||||
import { DateInputProps } from "../parts/DateInput";
|
||||
import {
|
||||
formatDateForInput,
|
||||
getLocaleFromString,
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
isValidDate,
|
||||
parseDate,
|
||||
} from "../utils";
|
||||
import { useEscape } from "./useEscape";
|
||||
import { useOutsideClickHandler } from "./useOutsideClickHandler";
|
||||
|
||||
export interface UseMonthPickerOptions
|
||||
extends Pick<
|
||||
@@ -45,8 +43,8 @@ export interface UseMonthPickerOptions
|
||||
*/
|
||||
allowTwoDigitYear?: boolean;
|
||||
/**
|
||||
* Opens datepicker on input-focus
|
||||
* @default true
|
||||
* Will be removed in a future major-version
|
||||
* @deprecated
|
||||
*/
|
||||
openOnFocus?: boolean;
|
||||
}
|
||||
@@ -60,7 +58,12 @@ interface UseMonthPickerValue {
|
||||
* Use: <MonthPicker.Input {...inputProps} />
|
||||
*/
|
||||
inputProps: Pick<DateInputProps, "onChange" | "onFocus" | "value"> & {
|
||||
ref: React.RefObject<HTMLInputElement>;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setAnchorRef: React.Dispatch<
|
||||
React.SetStateAction<HTMLButtonElement | null>
|
||||
>;
|
||||
};
|
||||
/**
|
||||
* Currently selected Date
|
||||
@@ -138,17 +141,14 @@ export const useMonthpicker = (
|
||||
onValidate,
|
||||
defaultYear,
|
||||
allowTwoDigitYear = true,
|
||||
openOnFocus = true,
|
||||
} = opt;
|
||||
|
||||
const [anchorRef, setAnchorRef] = useState<HTMLButtonElement | null>(null);
|
||||
const [defaultSelected, setDefaultSelected] = useState(_defaultSelected);
|
||||
|
||||
const today = useMemo(() => new Date(), []);
|
||||
const locale = getLocaleFromString(_locale);
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [monthpickerRef, setMonthpickerRef] = useState<HTMLDivElement>();
|
||||
|
||||
// Initialize states
|
||||
const [year, setYear] = useState(defaultSelected ?? defaultYear ?? today);
|
||||
const [selectedMonth, setSelectedMonth] = useState(defaultSelected);
|
||||
@@ -161,21 +161,14 @@ export const useMonthpicker = (
|
||||
const [inputValue, setInputValue] = useState(defaultInputValue);
|
||||
|
||||
const handleOpen = useCallback(
|
||||
(open: boolean) => {
|
||||
setOpen(open);
|
||||
open && setYear(selectedMonth ?? defaultSelected ?? defaultYear ?? today);
|
||||
(newOpen: boolean) => {
|
||||
setOpen(newOpen);
|
||||
newOpen &&
|
||||
setYear(selectedMonth ?? defaultSelected ?? defaultYear ?? today);
|
||||
},
|
||||
[defaultSelected, defaultYear, selectedMonth, today]
|
||||
);
|
||||
|
||||
useOutsideClickHandler(open, handleOpen, [
|
||||
monthpickerRef,
|
||||
inputRef.current,
|
||||
inputRef.current?.nextSibling,
|
||||
]);
|
||||
|
||||
useEscape(open, handleOpen, inputRef);
|
||||
|
||||
const updateMonth = (date?: Date) => {
|
||||
onMonthChange?.(date);
|
||||
setSelectedMonth(date);
|
||||
@@ -203,7 +196,7 @@ export const useMonthpicker = (
|
||||
if (e.target.readOnly) {
|
||||
return;
|
||||
}
|
||||
!open && openOnFocus && handleOpen(true);
|
||||
|
||||
const day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
@@ -238,8 +231,8 @@ export const useMonthpicker = (
|
||||
const handleMonthClick = (month?: Date) => {
|
||||
if (month) {
|
||||
handleOpen(false);
|
||||
inputRef.current && inputRef.current.focus();
|
||||
setYear(month);
|
||||
anchorRef?.focus();
|
||||
}
|
||||
|
||||
if (!required && !month) {
|
||||
@@ -313,9 +306,11 @@ export const useMonthpicker = (
|
||||
toDate,
|
||||
open,
|
||||
onOpenToggle: () => handleOpen(!open),
|
||||
onClose: () => {
|
||||
handleOpen(false);
|
||||
anchorRef?.focus();
|
||||
},
|
||||
disabled,
|
||||
bubbleEscape: true,
|
||||
ref: setMonthpickerRef,
|
||||
};
|
||||
|
||||
const inputProps = {
|
||||
@@ -323,7 +318,7 @@ export const useMonthpicker = (
|
||||
onFocus: handleFocus,
|
||||
onBlur: handleBlur,
|
||||
value: inputValue,
|
||||
ref: inputRef,
|
||||
setAnchorRef,
|
||||
};
|
||||
|
||||
return { monthpickerProps, inputProps, reset, selectedMonth, setSelected };
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { useCallback, useEffect } from "react";
|
||||
|
||||
export const useOutsideClickHandler = (
|
||||
open: boolean,
|
||||
setOpen: (openState: boolean) => void,
|
||||
refs: Array<any>
|
||||
) => {
|
||||
const handleFocusIn = useCallback(
|
||||
(e) => {
|
||||
const composed = e.composedPath?.()?.[0];
|
||||
if (!e?.target || !e?.target?.nodeType || !composed) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!refs.some(
|
||||
(element) =>
|
||||
element?.contains(e.target) || element?.contains(composed)
|
||||
)
|
||||
) {
|
||||
open && setOpen(false);
|
||||
}
|
||||
},
|
||||
[open, refs, setOpen]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("focusin", handleFocusIn);
|
||||
window.addEventListener("pointerdown", handleFocusIn);
|
||||
return () => {
|
||||
window?.removeEventListener?.("focusin", handleFocusIn);
|
||||
window?.removeEventListener?.("pointerdown", handleFocusIn);
|
||||
};
|
||||
}, [handleFocusIn]);
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
import differenceInCalendarDays from "date-fns/differenceInCalendarDays";
|
||||
import isBefore from "date-fns/isBefore";
|
||||
import checkIsBefore from "date-fns/isBefore";
|
||||
import isWeekend from "date-fns/isWeekend";
|
||||
import React, { useRef, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { DateRange, isMatch } from "react-day-picker";
|
||||
import { DateInputProps } from "../DateInput";
|
||||
import { DatePickerProps } from "../datepicker/DatePicker";
|
||||
import { DateInputProps } from "../parts/DateInput";
|
||||
import {
|
||||
formatDateForInput,
|
||||
getLocaleFromString,
|
||||
@@ -12,8 +12,6 @@ import {
|
||||
parseDate,
|
||||
} from "../utils";
|
||||
import { DateValidationT, UseDatepickerOptions } from "./useDatepicker";
|
||||
import { useEscape } from "./useEscape";
|
||||
import { useOutsideClickHandler } from "./useOutsideClickHandler";
|
||||
|
||||
export type RangeValidationT = {
|
||||
from: DateValidationT;
|
||||
@@ -74,14 +72,28 @@ interface UseRangeDatepickerValue {
|
||||
fromInputProps: Pick<
|
||||
DateInputProps,
|
||||
"onChange" | "onFocus" | "onBlur" | "value"
|
||||
> & { ref: React.RefObject<HTMLInputElement> };
|
||||
> & {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setAnchorRef: React.Dispatch<
|
||||
React.SetStateAction<HTMLButtonElement | null>
|
||||
>;
|
||||
};
|
||||
/**
|
||||
* Use: <DatePicker.Input label="to" {...toInputProps}/>
|
||||
*/
|
||||
toInputProps: Pick<
|
||||
DateInputProps,
|
||||
"onChange" | "onFocus" | "onBlur" | "value"
|
||||
> & { ref: React.RefObject<HTMLInputElement> };
|
||||
> & {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setAnchorRef?: React.Dispatch<
|
||||
React.SetStateAction<HTMLButtonElement | null>
|
||||
>;
|
||||
};
|
||||
/**
|
||||
* Resets all states (callback)
|
||||
*/
|
||||
@@ -120,15 +132,15 @@ const fromValidation = (day: Date, opt?: UseRangeDatepickerOptions) => {
|
||||
isInvalid: !isValidDate(day),
|
||||
isWeekend: opt?.disableWeekends && isWeekend(day),
|
||||
isDisabled: opt?.disabled && isMatch(day, opt.disabled),
|
||||
isBefore: isBefore,
|
||||
isAfter: isAfter,
|
||||
isBefore,
|
||||
isAfter,
|
||||
};
|
||||
}
|
||||
if (isBefore || isAfter) {
|
||||
return {
|
||||
isValidDate: false,
|
||||
isBefore: isBefore,
|
||||
isAfter: isAfter,
|
||||
isBefore,
|
||||
isAfter,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -218,14 +230,11 @@ export const useRangeDatepicker = (
|
||||
onValidate,
|
||||
defaultMonth,
|
||||
allowTwoDigitYear = true,
|
||||
openOnFocus = true,
|
||||
} = opt;
|
||||
|
||||
const locale = getLocaleFromString(_locale);
|
||||
const [anchorRef, setAnchorRef] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const inputRefTo = useRef<HTMLInputElement>(null);
|
||||
const inputRefFrom = useRef<HTMLInputElement>(null);
|
||||
const [daypickerRef, setDaypickerRef] = useState<HTMLDivElement>();
|
||||
const locale = getLocaleFromString(_locale);
|
||||
|
||||
const [defaultSelected, setDefaultSelected] = useState(_defaultSelected);
|
||||
|
||||
@@ -255,20 +264,6 @@ export const useRangeDatepicker = (
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useOutsideClickHandler(open, setOpen, [
|
||||
daypickerRef,
|
||||
inputRefTo.current,
|
||||
inputRefFrom.current,
|
||||
inputRefTo.current?.nextSibling,
|
||||
inputRefFrom.current?.nextSibling,
|
||||
]);
|
||||
|
||||
useEscape(
|
||||
open,
|
||||
setOpen,
|
||||
selectedRange?.from && !selectedRange?.to ? inputRefTo : inputRefFrom
|
||||
);
|
||||
|
||||
const updateRange = (range?: DateRange) => {
|
||||
onRangeChange?.(range);
|
||||
setSelectedRange(range);
|
||||
@@ -324,7 +319,6 @@ export const useRangeDatepicker = (
|
||||
if (e.target.readOnly) {
|
||||
return;
|
||||
}
|
||||
!open && openOnFocus && setOpen(true);
|
||||
const day = parseDate(
|
||||
e.target.value,
|
||||
today,
|
||||
@@ -378,11 +372,12 @@ export const useRangeDatepicker = (
|
||||
const handleSelect = (range) => {
|
||||
if (range?.from && range?.to) {
|
||||
setOpen(false);
|
||||
anchorRef?.focus();
|
||||
}
|
||||
const prevToRange =
|
||||
!selectedRange?.from && selectedRange?.to ? selectedRange?.to : range?.to;
|
||||
|
||||
const resetTo = isBefore(prevToRange, range?.from);
|
||||
const resetTo = checkIsBefore(prevToRange, range?.from);
|
||||
|
||||
range?.from
|
||||
? setFromInputValue(
|
||||
@@ -534,8 +529,8 @@ export const useRangeDatepicker = (
|
||||
};
|
||||
|
||||
const datepickerProps = {
|
||||
month: month,
|
||||
onMonthChange: (month) => setMonth(month),
|
||||
month,
|
||||
onMonthChange: setMonth,
|
||||
onSelect: handleSelect,
|
||||
selected: selectedRange,
|
||||
locale: _locale,
|
||||
@@ -545,10 +540,12 @@ export const useRangeDatepicker = (
|
||||
mode: "range" as const,
|
||||
open,
|
||||
onOpenToggle: () => setOpen((x) => !x),
|
||||
onClose: () => {
|
||||
setOpen(false);
|
||||
anchorRef?.focus();
|
||||
},
|
||||
disabled,
|
||||
disableWeekends,
|
||||
bubbleEscape: true,
|
||||
ref: setDaypickerRef,
|
||||
};
|
||||
|
||||
const fromInputProps = {
|
||||
@@ -556,7 +553,7 @@ export const useRangeDatepicker = (
|
||||
onFocus: (e) => handleFocus(e, RANGE.FROM),
|
||||
onBlur: (e) => handleBlur(e, RANGE.FROM),
|
||||
value: fromInputValue,
|
||||
ref: inputRefFrom,
|
||||
setAnchorRef,
|
||||
};
|
||||
|
||||
const toInputProps = {
|
||||
@@ -564,7 +561,7 @@ export const useRangeDatepicker = (
|
||||
onFocus: (e) => handleFocus(e, RANGE.TO),
|
||||
onBlur: (e) => handleBlur(e, RANGE.TO),
|
||||
value: toInputValue,
|
||||
ref: inputRefTo,
|
||||
setAnchorRef,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
export { type DateInputProps } from "./DateInput";
|
||||
export {
|
||||
type DatePickerProps,
|
||||
default as DatePicker,
|
||||
type DatePickerProps,
|
||||
} from "./datepicker/DatePicker";
|
||||
export { type DatePickerStandaloneProps } from "./datepicker/DatePickerStandalone";
|
||||
export { type MonthPickerStandaloneProps } from "./monthpicker/MonthPickerStandalone";
|
||||
export {
|
||||
useDatepicker,
|
||||
useMonthpicker,
|
||||
useRangeDatepicker,
|
||||
type RangeValidationT,
|
||||
type DateValidationT,
|
||||
type MonthValidationT,
|
||||
type RangeValidationT,
|
||||
} from "./hooks";
|
||||
export {
|
||||
default as MonthPicker,
|
||||
type MonthPickerProps,
|
||||
} from "./monthpicker/MonthPicker";
|
||||
export { default as MonthPicker } from "./monthpicker/MonthPicker";
|
||||
export { type MonthPickerStandaloneProps } from "./monthpicker/MonthPickerStandalone";
|
||||
export { type MonthPickerProps } from "./monthpicker/types";
|
||||
export { type DateInputProps } from "./parts/DateInput";
|
||||
@@ -1,8 +1,8 @@
|
||||
import cl from "clsx";
|
||||
import format from "date-fns/format";
|
||||
import isSameMonth from "date-fns/isSameMonth";
|
||||
import compareAsc from "date-fns/compareAsc";
|
||||
import compareDesc from "date-fns/compareDesc";
|
||||
import format from "date-fns/format";
|
||||
import isSameMonth from "date-fns/isSameMonth";
|
||||
import setYear from "date-fns/setYear";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { useDayPicker } from "react-day-picker";
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "@navikt/aksel-icons";
|
||||
import { isAfter, isBefore } from "date-fns";
|
||||
import setYear from "date-fns/setYear";
|
||||
import startOfMonth from "date-fns/startOfMonth";
|
||||
import startOfYear from "date-fns/startOfYear";
|
||||
import React from "react";
|
||||
import { useDayPicker } from "react-day-picker";
|
||||
import { Button, Select } from "../..";
|
||||
import { Button } from "../../button";
|
||||
import { Select } from "../../form/Select";
|
||||
import { useSharedMonthContext } from "../context";
|
||||
import { labelNextYear, labelPrevYear } from "../utils";
|
||||
import { isAfter, isBefore } from "date-fns";
|
||||
|
||||
export const MonthCaption = () => {
|
||||
const {
|
||||
@@ -23,9 +24,9 @@ export const MonthCaption = () => {
|
||||
|
||||
if (hasDropdown && fromDate && toDate) {
|
||||
const fromYear = fromDate.getFullYear();
|
||||
const toYear = toDate.getFullYear();
|
||||
for (let year = fromYear; year <= toYear; year++) {
|
||||
years.push(setYear(startOfYear(new Date()), year));
|
||||
const toDateYear = toDate.getFullYear();
|
||||
for (let currYear = fromYear; currYear <= toDateYear; currYear++) {
|
||||
years.push(setYear(startOfYear(new Date()), currYear));
|
||||
}
|
||||
|
||||
if (!years.map((x) => x.getFullYear()).includes(year.getFullYear())) {
|
||||
@@ -74,9 +75,9 @@ export const MonthCaption = () => {
|
||||
onChange={handleYearChange}
|
||||
className="navds-date__caption__year"
|
||||
>
|
||||
{years.map((year) => (
|
||||
<option key={year.getFullYear()} value={year.getFullYear()}>
|
||||
{formatYearCaption(year, { locale })}
|
||||
{years.map((yearOpt) => (
|
||||
<option key={yearOpt.getFullYear()} value={yearOpt.getFullYear()}>
|
||||
{formatYearCaption(yearOpt, { locale })}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@@ -1,97 +1,15 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useRef, useState } from "react";
|
||||
import React, { forwardRef, useMemo, useRef, useState } from "react";
|
||||
import { RootProvider } from "react-day-picker";
|
||||
import { Popover, useId } from "../..";
|
||||
import { DateInputProps, MonthPickerInput } from "../DateInput";
|
||||
import { mergeRefs, useId } from "../../util";
|
||||
import { DateContext, SharedMonthProvider } from "../context";
|
||||
import { getLocaleFromString, Matcher } from "../utils";
|
||||
import { MonthPickerInput } from "../parts/DateInput";
|
||||
import { DateWrapper } from "../parts/DateWrapper";
|
||||
import { getLocaleFromString } from "../utils";
|
||||
import MonthCaption from "./MonthCaption";
|
||||
import MonthPickerStandalone, {
|
||||
MonthPickerStandaloneType,
|
||||
} from "./MonthPickerStandalone";
|
||||
import MonthPickerStandalone from "./MonthPickerStandalone";
|
||||
import MonthSelector from "./MonthSelector";
|
||||
|
||||
export interface MonthPickerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Element monthpicker anchors to. Use <MonthPicker.Input /> for built-in toggle,
|
||||
* or make your own with the open/onClose props
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Classname for datepicker in popover
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Classname for wrapper
|
||||
*/
|
||||
wrapperClassName?: string;
|
||||
/**
|
||||
* The earliest month to start navigation.
|
||||
*/
|
||||
fromDate?: Date;
|
||||
/**
|
||||
* The latest day to end navigation.
|
||||
*/
|
||||
toDate?: Date;
|
||||
/**
|
||||
* Changes monthpicker locale
|
||||
* @default "nb" (norsk bokmål)
|
||||
*/
|
||||
locale?: "nb" | "nn" | "en";
|
||||
/**
|
||||
* Display dropdown for choosing year. Needs `fromDate` + `toDate` to work.
|
||||
* @default false
|
||||
*/
|
||||
dropdownCaption?: boolean;
|
||||
/**
|
||||
* Apply the disabled modifier to the matching months. Uses a subset of React Day Picker Matcher type. https://react-day-picker.js.org/api/types/Matcher
|
||||
*/
|
||||
disabled?: Matcher[];
|
||||
/**
|
||||
* Controlled selected-month
|
||||
*/
|
||||
selected?: Date;
|
||||
/**
|
||||
* Default selected month.
|
||||
*/
|
||||
defaultSelected?: Date;
|
||||
/**
|
||||
* Open state for user-controlled state. Component controlled by default
|
||||
*/
|
||||
open?: boolean;
|
||||
/**
|
||||
* onClose callback for user-controlled state
|
||||
*/
|
||||
onClose?: () => void;
|
||||
/**
|
||||
* onOpenToggle callback for user-controlled-state. Only called if `<MonthPicker.Input />` is used
|
||||
*/
|
||||
onOpenToggle?: () => void;
|
||||
/**
|
||||
* Callback for user-controlled state
|
||||
*/
|
||||
onMonthSelect?: (month?: Date) => void;
|
||||
/**
|
||||
* Used to set visible year programmatically. Component controlled by default
|
||||
*/
|
||||
year?: Date;
|
||||
/**
|
||||
* Event fired when the user navigates between years.
|
||||
*/
|
||||
onYearChange?: (y?: Date) => void;
|
||||
/**
|
||||
* Avoid using if possible!
|
||||
* Changes what CSS position property to use
|
||||
* You want to use "fixed" if parent wrapper has position relative, but you want popover to escape
|
||||
* @default "absolute"
|
||||
*/
|
||||
strategy?: "absolute" | "fixed";
|
||||
/**
|
||||
* Bubbles Escape keydown-event up trough DOM-tree. This is set to false by default to prevent closing components like Modal on Escape
|
||||
* @default false
|
||||
*/
|
||||
bubbleEscape?: boolean;
|
||||
}
|
||||
import { MonthPickerProps } from "./types";
|
||||
|
||||
interface MonthPickerComponent
|
||||
extends React.ForwardRefExoticComponent<MonthPickerProps> {
|
||||
@@ -105,14 +23,12 @@ interface MonthPickerComponent
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
Standalone: MonthPickerStandaloneType;
|
||||
Standalone: typeof MonthPickerStandalone;
|
||||
/**
|
||||
* Custom TextField for MonthPicker
|
||||
* @see 🏷️ {@link DateInputProps}
|
||||
*/
|
||||
Input: React.ForwardRefExoticComponent<
|
||||
DateInputProps & React.RefAttributes<HTMLInputElement>
|
||||
>;
|
||||
Input: typeof MonthPickerInput;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +74,6 @@ export const MonthPicker = forwardRef<HTMLDivElement, MonthPickerProps>(
|
||||
year,
|
||||
onYearChange,
|
||||
strategy = "absolute",
|
||||
bubbleEscape = false,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
@@ -166,6 +81,7 @@ export const MonthPicker = forwardRef<HTMLDivElement, MonthPickerProps>(
|
||||
const [open, setOpen] = useState(_open ?? false);
|
||||
|
||||
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
const mergedRef = useMemo(() => mergeRefs([wrapperRef, ref]), [ref]);
|
||||
|
||||
const [selectedMonth, setSelectedMonth] = useState<Date | undefined>(
|
||||
defaultSelected
|
||||
@@ -191,50 +107,47 @@ export const MonthPicker = forwardRef<HTMLDivElement, MonthPickerProps>(
|
||||
onOpenToggle?.();
|
||||
},
|
||||
ariaId,
|
||||
defined: true,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
ref={mergedRef}
|
||||
className={cl("navds-date__wrapper", wrapperClassName)}
|
||||
>
|
||||
{children}
|
||||
{(_open ?? open) && (
|
||||
<Popover
|
||||
arrow={false}
|
||||
anchorEl={wrapperRef.current}
|
||||
open={_open ?? open}
|
||||
onClose={() => onClose?.() ?? setOpen(false)}
|
||||
placement="bottom-start"
|
||||
role="dialog"
|
||||
ref={ref}
|
||||
id={ariaId}
|
||||
className="navds-date navds-date__popover"
|
||||
strategy={strategy}
|
||||
bubbleEscape={bubbleEscape}
|
||||
<DateWrapper
|
||||
open={_open ?? open}
|
||||
anchor={wrapperRef.current}
|
||||
onClose={() => onClose?.() ?? setOpen(false)}
|
||||
locale={locale}
|
||||
variant="month"
|
||||
popoverProps={{
|
||||
id: ariaId,
|
||||
strategy,
|
||||
}}
|
||||
>
|
||||
<RootProvider
|
||||
locale={getLocaleFromString(locale)}
|
||||
selected={selected ?? selectedMonth}
|
||||
toDate={toDate}
|
||||
fromDate={fromDate}
|
||||
month={selected ?? selectedMonth}
|
||||
>
|
||||
<RootProvider
|
||||
locale={getLocaleFromString(locale)}
|
||||
selected={selected ?? selectedMonth}
|
||||
toDate={toDate}
|
||||
fromDate={fromDate}
|
||||
month={selected ?? selectedMonth}
|
||||
>
|
||||
<div className={cl("rdp-month", className)}>
|
||||
<SharedMonthProvider
|
||||
dropdownCaption={dropdownCaption}
|
||||
disabled={disabled}
|
||||
selected={selected ?? selectedMonth}
|
||||
onSelect={handleSelect}
|
||||
year={year}
|
||||
onYearChange={onYearChange}
|
||||
>
|
||||
<MonthCaption />
|
||||
<MonthSelector />
|
||||
</SharedMonthProvider>
|
||||
</div>
|
||||
</RootProvider>
|
||||
</Popover>
|
||||
)}
|
||||
<div className={cl("rdp-month", className)}>
|
||||
<SharedMonthProvider
|
||||
dropdownCaption={dropdownCaption}
|
||||
disabled={disabled}
|
||||
selected={selected ?? selectedMonth}
|
||||
onSelect={handleSelect}
|
||||
year={year}
|
||||
onYearChange={onYearChange}
|
||||
>
|
||||
<MonthCaption />
|
||||
<MonthSelector />
|
||||
</SharedMonthProvider>
|
||||
</div>
|
||||
</RootProvider>
|
||||
</DateWrapper>
|
||||
</div>
|
||||
</DateContext.Provider>
|
||||
);
|
||||
|
||||
@@ -4,8 +4,8 @@ import { RootProvider } from "react-day-picker";
|
||||
import { SharedMonthProvider } from "../context";
|
||||
import { getLocaleFromString } from "../utils";
|
||||
import MonthCaption from "./MonthCaption";
|
||||
import { MonthPickerProps } from "./MonthPicker";
|
||||
import MonthSelector from "./MonthSelector";
|
||||
import { MonthPickerProps } from "./types";
|
||||
|
||||
export interface MonthPickerStandaloneProps
|
||||
extends Omit<
|
||||
|
||||
@@ -3,7 +3,7 @@ import setMonth from "date-fns/setMonth";
|
||||
import setYear from "date-fns/setYear";
|
||||
import startOfMonth from "date-fns/startOfMonth";
|
||||
import React, { useState } from "react";
|
||||
import { BodyShort } from "../..";
|
||||
import { BodyShort } from "../../typography";
|
||||
import { useSharedMonthContext } from "../context";
|
||||
import { isMatch } from "../utils";
|
||||
import MonthButton from "./MonthButton";
|
||||
@@ -62,9 +62,9 @@ export const MonthSelector = () => {
|
||||
return (
|
||||
<BodyShort as="table" className="rdp-table">
|
||||
<tbody className="rdp-tbody">
|
||||
{tableMonths.map((months, i) => (
|
||||
{tableMonths.map((tableRow, i) => (
|
||||
<tr className="rdp-row" key={i}>
|
||||
{months.map((month: Date) => {
|
||||
{tableRow.map((month: Date) => {
|
||||
return (
|
||||
<td key={month.toDateString()} className="rdp-cell">
|
||||
<MonthButton
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Meta, StoryFn } from "@storybook/react";
|
||||
import setYear from "date-fns/setYear";
|
||||
import React, { useId, useState } from "react";
|
||||
import { Button, DateInputProps } from "../..";
|
||||
import { useMonthpicker } from "../hooks";
|
||||
import MonthPicker, { MonthPickerProps } from "./MonthPicker";
|
||||
import MonthPicker from "./MonthPicker";
|
||||
import { MonthPickerProps } from "./types";
|
||||
|
||||
export default {
|
||||
title: "ds-react/Monthpicker",
|
||||
@@ -147,3 +149,28 @@ export const UserControlled = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const FollowYear = () => {
|
||||
const { monthpickerProps, inputProps, selectedMonth, setSelected } =
|
||||
useMonthpicker({
|
||||
fromDate: new Date("Aug 23 2019"),
|
||||
toDate: new Date("Aug 23 2025"),
|
||||
onMonthChange: console.log,
|
||||
});
|
||||
|
||||
const customYearChange = (yearDate?: Date) => {
|
||||
monthpickerProps.onYearChange?.(yearDate);
|
||||
if (selectedMonth && yearDate) {
|
||||
setSelected(setYear(selectedMonth, yearDate.getFullYear()));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-96">
|
||||
<MonthPicker {...monthpickerProps} onYearChange={customYearChange}>
|
||||
<MonthPicker.Input {...inputProps} label="Velg månede" />
|
||||
</MonthPicker>
|
||||
{selectedMonth && <div className="pt-4">{selectedMonth.getMonth()}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Matcher } from "../utils";
|
||||
|
||||
export interface MonthPickerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Element monthpicker anchors to. Use <MonthPicker.Input /> for built-in toggle,
|
||||
* or make your own with the open/onClose props
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Classname for datepicker in popover
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Classname for wrapper
|
||||
*/
|
||||
wrapperClassName?: string;
|
||||
/**
|
||||
* The earliest month to start navigation.
|
||||
*/
|
||||
fromDate?: Date;
|
||||
/**
|
||||
* The latest day to end navigation.
|
||||
*/
|
||||
toDate?: Date;
|
||||
/**
|
||||
* Changes monthpicker locale
|
||||
* @default "nb" (norsk bokmål)
|
||||
*/
|
||||
locale?: "nb" | "nn" | "en";
|
||||
/**
|
||||
* Display dropdown for choosing year. Needs `fromDate` + `toDate` to work.
|
||||
* @default false
|
||||
*/
|
||||
dropdownCaption?: boolean;
|
||||
/**
|
||||
* Apply the disabled modifier to the matching months. Uses a subset of React Day Picker Matcher type. https://react-day-picker.js.org/api/types/Matcher
|
||||
*/
|
||||
disabled?: Matcher[];
|
||||
/**
|
||||
* Controlled selected-month
|
||||
*/
|
||||
selected?: Date;
|
||||
/**
|
||||
* Default selected month.
|
||||
*/
|
||||
defaultSelected?: Date;
|
||||
/**
|
||||
* Open state for user-controlled state. Component controlled by default
|
||||
*/
|
||||
open?: boolean;
|
||||
/**
|
||||
* onClose callback for user-controlled state
|
||||
*/
|
||||
onClose?: () => void;
|
||||
/**
|
||||
* onOpenToggle callback for user-controlled-state. Only called if `<MonthPicker.Input />` is used
|
||||
*/
|
||||
onOpenToggle?: () => void;
|
||||
/**
|
||||
* Callback for user-controlled state
|
||||
*/
|
||||
onMonthSelect?: (month?: Date) => void;
|
||||
/**
|
||||
* Used to set visible year programmatically. Component controlled by default
|
||||
*/
|
||||
year?: Date;
|
||||
/**
|
||||
* Event fired when the user navigates between years.
|
||||
*/
|
||||
onYearChange?: (y?: Date) => void;
|
||||
/**
|
||||
* Avoid using if possible!
|
||||
* Changes what CSS position property to use
|
||||
* You want to use "fixed" if parent wrapper has position relative, but you want popover to escape
|
||||
* @default "absolute"
|
||||
*/
|
||||
strategy?: "absolute" | "fixed";
|
||||
}
|
||||
+23
-11
@@ -1,10 +1,11 @@
|
||||
import { CalendarIcon } from "@navikt/aksel-icons";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, InputHTMLAttributes } from "react";
|
||||
import { BodyShort, ErrorMessage, Label, omit } from "..";
|
||||
import { FormFieldProps, useFormField } from "../form/useFormField";
|
||||
import { useDateInputContext } from "./context";
|
||||
import { ReadOnlyIcon } from "../form/ReadOnlyIcon";
|
||||
import React, { forwardRef, InputHTMLAttributes, useRef } from "react";
|
||||
import { ReadOnlyIcon } from "../../form/ReadOnlyIcon";
|
||||
import { FormFieldProps, useFormField } from "../../form/useFormField";
|
||||
import { BodyShort, ErrorMessage, Label } from "../../typography";
|
||||
import { omit } from "../../util";
|
||||
import { useDateInputContext } from "../context";
|
||||
|
||||
export interface DateInputProps
|
||||
extends FormFieldProps,
|
||||
@@ -27,6 +28,10 @@ export interface DateInputProps
|
||||
* @private
|
||||
*/
|
||||
variant?: "datepicker" | "monthpicker";
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setAnchorRef?: React.Dispatch<React.SetStateAction<HTMLButtonElement | null>>;
|
||||
}
|
||||
|
||||
const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
||||
@@ -36,9 +41,12 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
||||
label,
|
||||
description,
|
||||
variant = "datepicker",
|
||||
setAnchorRef,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const isDatepickerVariant = variant === "datepicker";
|
||||
|
||||
const conditionalVariables = {
|
||||
@@ -49,7 +57,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
||||
},
|
||||
};
|
||||
|
||||
const { onOpen, ariaId, open } = useDateInputContext();
|
||||
const context = useDateInputContext();
|
||||
|
||||
const {
|
||||
inputProps,
|
||||
@@ -107,7 +115,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
||||
{...omit(rest, ["error", "errorId", "size"])}
|
||||
{...inputProps}
|
||||
autoComplete="off"
|
||||
aria-controls={open ? ariaId : undefined}
|
||||
aria-controls={context?.open ? context.ariaId : undefined}
|
||||
readOnly={readOnly}
|
||||
className={cl(
|
||||
"navds-date__field-input",
|
||||
@@ -115,19 +123,23 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
||||
"navds-body-short",
|
||||
`navds-body-short--${size}`
|
||||
)}
|
||||
size={14}
|
||||
size={isDatepickerVariant ? 11 : 14}
|
||||
/>
|
||||
<button
|
||||
disabled={inputProps.disabled || readOnly}
|
||||
tabIndex={readOnly ? -1 : open ? -1 : 0}
|
||||
onClick={() => onOpen()}
|
||||
tabIndex={readOnly ? -1 : context?.open ? -1 : 0}
|
||||
onClick={() => {
|
||||
context?.onOpen();
|
||||
setAnchorRef?.(buttonRef.current);
|
||||
}}
|
||||
type="button"
|
||||
className="navds-date__field-button"
|
||||
ref={buttonRef}
|
||||
>
|
||||
<CalendarIcon
|
||||
pointerEvents="none"
|
||||
title={
|
||||
open
|
||||
context?.open
|
||||
? conditionalVariables.iconTitle.close
|
||||
: conditionalVariables.iconTitle.open
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import cl from "clsx";
|
||||
import React, { useContext, useRef } from "react";
|
||||
import { Button } from "../../button";
|
||||
import { Modal } from "../../modal";
|
||||
import { ModalContext } from "../../modal/ModalContext";
|
||||
import { Popover } from "../../popover";
|
||||
import { useMedia } from "../../util/useMedia";
|
||||
import { modalCloseButtonLabel, modalLabel } from "../utils/labels";
|
||||
|
||||
type DateWrapperProps = {
|
||||
open: boolean;
|
||||
children: React.ReactNode;
|
||||
onClose: () => void;
|
||||
anchor: HTMLDivElement | null;
|
||||
locale: "nb" | "nn" | "en";
|
||||
variant: "single" | "multiple" | "range" | "month";
|
||||
popoverProps: {
|
||||
id?: string;
|
||||
strategy?: "absolute" | "fixed";
|
||||
};
|
||||
};
|
||||
|
||||
export const DateWrapper = ({
|
||||
open,
|
||||
children,
|
||||
onClose,
|
||||
anchor,
|
||||
locale,
|
||||
variant,
|
||||
popoverProps,
|
||||
}: DateWrapperProps) => {
|
||||
const modalRef = useRef<HTMLDialogElement>(null);
|
||||
const isInModal = useContext(ModalContext) !== null;
|
||||
const hideModal =
|
||||
useMedia("screen and (min-width: 768px)", true) && !isInModal;
|
||||
|
||||
if (hideModal) {
|
||||
return (
|
||||
<Popover
|
||||
arrow={false}
|
||||
anchorEl={anchor}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
placement="bottom-start"
|
||||
role="dialog"
|
||||
className={cl("navds-date__popover", {
|
||||
"navds-date": variant === "month",
|
||||
})}
|
||||
flip={false}
|
||||
{...popoverProps}
|
||||
>
|
||||
{children}
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Modal
|
||||
ref={modalRef}
|
||||
open={open}
|
||||
onClose={(event) => {
|
||||
event.stopPropagation();
|
||||
onClose();
|
||||
}}
|
||||
aria-label={modalLabel(locale, variant)}
|
||||
className={cl("navds-date__modal", {
|
||||
"navds-date__nested-modal": isInModal,
|
||||
"navds-date": variant === "month",
|
||||
})}
|
||||
closeOnBackdropClick
|
||||
>
|
||||
<div className="navds-date__modal-body">
|
||||
{children}
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={() => modalRef?.current?.close()}
|
||||
size="small"
|
||||
>
|
||||
{modalCloseButtonLabel(locale)}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -1,13 +1,12 @@
|
||||
/* https://github.com/gpbl/react-day-picker/blob/main/src/components/Table/utils/getMonthWeeks.test.ts */
|
||||
import { nb, enGB } from "date-fns/locale";
|
||||
import { enGB, nb } from "date-fns/locale";
|
||||
|
||||
import { getMonthWeeks } from "../get-month-weeks";
|
||||
|
||||
describe('when using the "nB" locale', () => {
|
||||
const locale = nb;
|
||||
describe("when getting the weeks for January 2022", () => {
|
||||
const date = new Date(2022, 0);
|
||||
const weeks = getMonthWeeks(date, { locale });
|
||||
const weeks = getMonthWeeks(new Date(2022, 0), { locale });
|
||||
test("the first week should be the last of the previous year", () => {
|
||||
const weekNumbers = weeks.map((week) => week.weekNumber);
|
||||
expect(weekNumbers[0]).toEqual(52);
|
||||
@@ -29,8 +28,10 @@ describe('when using the "enGB" locale', () => {
|
||||
describe("when using fixed weeks", () => {
|
||||
const useFixedWeeks = true;
|
||||
describe("when getting the weeks for December 2022", () => {
|
||||
const date = new Date(2022, 11);
|
||||
const weeks = getMonthWeeks(date, { useFixedWeeks, locale });
|
||||
const weeks = getMonthWeeks(new Date(2022, 11), {
|
||||
useFixedWeeks,
|
||||
locale,
|
||||
});
|
||||
test("should return 48 - 1 week numbers", () => {
|
||||
const weekNumbers = weeks.map((week) => week.weekNumber);
|
||||
const expectedResult = [48, 49, 50, 51, 52, 1];
|
||||
@@ -67,8 +68,10 @@ describe('when using the "nb" locale', () => {
|
||||
describe("when using fixed weeks", () => {
|
||||
const useFixedWeeks = true;
|
||||
describe("when getting the weeks for December 2022", () => {
|
||||
const date = new Date(2022, 11);
|
||||
const weeks = getMonthWeeks(date, { useFixedWeeks, locale });
|
||||
const weeks = getMonthWeeks(new Date(2022, 11), {
|
||||
useFixedWeeks,
|
||||
locale,
|
||||
});
|
||||
test("should return 48 - 1 week numbers", () => {
|
||||
const weekNumbers = weeks.map((week) => week.weekNumber);
|
||||
const expectedResult = [48, 49, 50, 51, 52, 1];
|
||||
|
||||
@@ -133,3 +133,86 @@ export const labelWeek = (localeCode?: string): string => {
|
||||
return `Uke:`;
|
||||
}
|
||||
};
|
||||
|
||||
const modalLabelSingle = (localeCode?: string): string => {
|
||||
switch (localeCode) {
|
||||
case "nb":
|
||||
return `Velg dato`;
|
||||
case "nn":
|
||||
return `Vel dato`;
|
||||
case "en-GB":
|
||||
return `Pick a date`;
|
||||
default:
|
||||
return `Velg dato`;
|
||||
}
|
||||
};
|
||||
|
||||
const modalLabelMultiple = (localeCode?: string): string => {
|
||||
switch (localeCode) {
|
||||
case "nb":
|
||||
return `Velg datoer`;
|
||||
case "nn":
|
||||
return `Vel datoar`;
|
||||
case "en-GB":
|
||||
return `Pick dates`;
|
||||
default:
|
||||
return `Velg datoer`;
|
||||
}
|
||||
};
|
||||
|
||||
const modalLabelRanged = (localeCode?: string): string => {
|
||||
switch (localeCode) {
|
||||
case "nb":
|
||||
return `Velg start- og sluttdato`;
|
||||
case "nn":
|
||||
return `Vel start- og sluttdato`;
|
||||
case "en-GB":
|
||||
return `Pick a start and end date`;
|
||||
default:
|
||||
return `Velg start- og sluttdato`;
|
||||
}
|
||||
};
|
||||
|
||||
const modalLabelMonth = (localeCode?: string): string => {
|
||||
switch (localeCode) {
|
||||
case "nb":
|
||||
return `Velg måned`;
|
||||
case "nn":
|
||||
return `Vel månad`;
|
||||
case "en-GB":
|
||||
return `Pick a month`;
|
||||
default:
|
||||
return `Velg måned`;
|
||||
}
|
||||
};
|
||||
|
||||
export const modalLabel = (
|
||||
localeCode: string,
|
||||
variant: "single" | "multiple" | "range" | "month"
|
||||
) => {
|
||||
switch (variant) {
|
||||
case "single":
|
||||
return modalLabelSingle(localeCode);
|
||||
case "multiple":
|
||||
return modalLabelMultiple(localeCode);
|
||||
case "range":
|
||||
return modalLabelRanged(localeCode);
|
||||
case "month":
|
||||
return modalLabelMonth(localeCode);
|
||||
default:
|
||||
return modalLabelSingle(localeCode);
|
||||
}
|
||||
};
|
||||
|
||||
export const modalCloseButtonLabel = (localeCode?: string): string => {
|
||||
switch (localeCode) {
|
||||
case "nb":
|
||||
return `Lukk`;
|
||||
case "nn":
|
||||
return `Lukk`;
|
||||
case "en-GB":
|
||||
return `Close`;
|
||||
default:
|
||||
return `Lukk`;
|
||||
}
|
||||
};
|
||||
@@ -15,27 +15,27 @@ export const nextEnabled = (
|
||||
const currentIndex = currentMonth.getMonth();
|
||||
|
||||
if (key === "Home") {
|
||||
const nextEnabled = nextOnRow(
|
||||
const nextDate = nextOnRow(
|
||||
currentIndex,
|
||||
months,
|
||||
yearState,
|
||||
disabled,
|
||||
"home"
|
||||
);
|
||||
if (nextEnabled) {
|
||||
return nextEnabled;
|
||||
if (nextDate) {
|
||||
return nextDate;
|
||||
}
|
||||
}
|
||||
if (key === "End") {
|
||||
const nextEnabled = nextOnRow(
|
||||
const nextDate = nextOnRow(
|
||||
currentIndex,
|
||||
months,
|
||||
yearState,
|
||||
disabled,
|
||||
"end"
|
||||
);
|
||||
if (nextEnabled) {
|
||||
return nextEnabled;
|
||||
if (nextDate) {
|
||||
return nextDate;
|
||||
}
|
||||
}
|
||||
if (key === "PageUp") {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import parse from "date-fns/parse";
|
||||
import isBefore from "date-fns/isBefore";
|
||||
import sub from "date-fns/sub";
|
||||
import { isValidDate } from ".";
|
||||
import { isValidDate } from "./check-dates";
|
||||
|
||||
export const INPUT_DATE_STRING_FORMAT_DATE = "dd.MM.yyyy";
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { createContext, useState } from "react";
|
||||
import Toggle, { ToggleProps } from "./Toggle";
|
||||
import React, { useState } from "react";
|
||||
import Menu, { MenuType } from "./Menu";
|
||||
import Toggle, { ToggleProps } from "./Toggle";
|
||||
import { DropdownContext } from "./context";
|
||||
|
||||
export interface DropdownProps {
|
||||
children: React.ReactNode;
|
||||
@@ -36,16 +37,6 @@ export interface DropdownType extends React.FC<DropdownProps> {
|
||||
Menu: MenuType;
|
||||
}
|
||||
|
||||
export interface DropdownContextType {
|
||||
readonly isOpen: boolean;
|
||||
handleToggle: (v: boolean) => void;
|
||||
readonly anchorEl: Element | null;
|
||||
setAnchorEl: React.Dispatch<React.SetStateAction<Element | null>>;
|
||||
onSelect: (element: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
export const DropdownContext = createContext<DropdownContextType | null>(null);
|
||||
|
||||
/**
|
||||
* A component that displays a dropdown menu when the user clicks on its toggle button.
|
||||
*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { OverridableComponent } from "../../../util/OverridableComponent";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { DropdownContext } from "../../Dropdown";
|
||||
import { OverridableComponent } from "../../../util";
|
||||
import { DropdownContext } from "../../context";
|
||||
|
||||
export interface GroupedItemProps
|
||||
extends React.ButtonHTMLAttributes<HTMLElement> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import { OverridableComponent } from "../../../util/OverridableComponent";
|
||||
import { OverridableComponent } from "../../../util";
|
||||
import GroupedHeading, { GroupedHeadingProps } from "./GroupedHeading";
|
||||
import GroupedItem, { GroupedItemProps } from "./GroupedItem";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import cl from "clsx";
|
||||
import { OverridableComponent } from "../../../util/OverridableComponent";
|
||||
import { DropdownContext } from "../../Dropdown";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { OverridableComponent } from "../../../util";
|
||||
import { DropdownContext } from "../../context";
|
||||
|
||||
export interface ListItemProps extends React.ButtonHTMLAttributes<HTMLElement> {
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import { OverridableComponent } from "../../../util/OverridableComponent";
|
||||
import { OverridableComponent } from "../../../util";
|
||||
import ListItem, { ListItemProps } from "./Item";
|
||||
|
||||
export interface ListProps extends React.HTMLAttributes<HTMLUListElement> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { Popover } from "../../popover";
|
||||
import { DropdownContext } from "../Dropdown";
|
||||
import { DropdownContext } from "../context";
|
||||
import Divider from "./Divider";
|
||||
import GroupedList, { GroupedListType } from "./GroupedList";
|
||||
import List, { ListType } from "./List";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import cl from "clsx";
|
||||
import { DropdownContext } from "./Dropdown";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { DropdownContext } from "./context";
|
||||
|
||||
export interface ToggleProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
export interface DropdownContextType {
|
||||
readonly isOpen: boolean;
|
||||
handleToggle: (v: boolean) => void;
|
||||
readonly anchorEl: Element | null;
|
||||
setAnchorEl: React.Dispatch<React.SetStateAction<Element | null>>;
|
||||
onSelect: (element: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
export const DropdownContext = createContext<DropdownContextType | null>(null);
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "clsx";
|
||||
import React, { createContext, forwardRef, useRef, useState } from "react";
|
||||
import { OverridableComponent } from "../util/OverridableComponent";
|
||||
import React, { forwardRef, useRef, useState } from "react";
|
||||
import { OverridableComponent } from "../util";
|
||||
import ExpansionCardContent, {
|
||||
ExpansionCardContentProps,
|
||||
} from "./ExpansionCardContent";
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
ExpansionCardTitle,
|
||||
ExpansionCardTitleProps,
|
||||
} from "./ExpansionCardTitle";
|
||||
import { ExpansionCardContext } from "./context";
|
||||
|
||||
interface ExpansionCardComponent
|
||||
extends React.ForwardRefExoticComponent<
|
||||
@@ -82,18 +83,6 @@ type ExpansionCardConditionalProps =
|
||||
"aria-labelledby": string;
|
||||
};
|
||||
|
||||
export type ExpansionCardContextProps = {
|
||||
open: boolean;
|
||||
toggleOpen: () => void;
|
||||
size: "medium" | "small";
|
||||
};
|
||||
|
||||
export const ExpansionCardContext = createContext<ExpansionCardContextProps>({
|
||||
open: false,
|
||||
toggleOpen: () => {},
|
||||
size: "medium",
|
||||
});
|
||||
|
||||
export type ExpansionCardProps = ExpansionCardCommonProps &
|
||||
ExpansionCardConditionalProps;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { BodyLong } from "../typography/BodyLong";
|
||||
import { ExpansionCardContext } from "./ExpansionCard";
|
||||
import { BodyLong } from "../typography";
|
||||
import { ExpansionCardContext } from "./context";
|
||||
|
||||
export interface ExpansionCardContentProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import cl from "clsx";
|
||||
import { BodyLong } from "../typography/BodyLong";
|
||||
import { ExpansionCardContext } from "./ExpansionCard";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { BodyLong } from "../typography";
|
||||
import { ExpansionCardContext } from "./context";
|
||||
|
||||
export interface ExpansionCardDescriptionProps
|
||||
extends React.HTMLAttributes<HTMLParagraphElement> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ChevronDownIcon } from "@navikt/aksel-icons";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { ExpansionCardContext } from "./ExpansionCard";
|
||||
import { ChevronDownIcon } from "@navikt/aksel-icons";
|
||||
import { ExpansionCardContext } from "./context";
|
||||
|
||||
export interface ExpansionCardHeaderProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "clsx";
|
||||
import { OverridableComponent } from "../util/OverridableComponent";
|
||||
import React, { forwardRef } from "react";
|
||||
import { OverridableComponent } from "../util";
|
||||
|
||||
export interface ExpansionCardTitleProps
|
||||
extends React.HTMLAttributes<HTMLHeadingElement> {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
export type ExpansionCardContextProps = {
|
||||
open: boolean;
|
||||
toggleOpen: () => void;
|
||||
size: "medium" | "small";
|
||||
};
|
||||
|
||||
export const ExpansionCardContext = createContext<ExpansionCardContextProps>({
|
||||
open: false,
|
||||
toggleOpen: () => {},
|
||||
size: "medium",
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "clsx";
|
||||
import { BodyLong, Checkbox, CheckboxProps, ErrorMessage, useId } from "..";
|
||||
import React, { forwardRef } from "react";
|
||||
import { BodyLong, ErrorMessage } from "../typography";
|
||||
import { useId } from "../util";
|
||||
import { Checkbox, CheckboxProps } from "./checkbox";
|
||||
import { useFormField } from "./useFormField";
|
||||
|
||||
export interface ConfirmationPanelProps
|
||||
|
||||
Loaded 100 of 834 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user