mirror of
https://github.com/navikt/aksel.git
synced 2026-10-13 09:12:03 +00:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b1424daf2 | ||
|
|
318b8c8ac6 | ||
|
|
4dd746d27d | ||
|
|
ffcfe79c18 | ||
|
|
5d8d2ffba9 | ||
|
|
c785316086 | ||
|
|
7087f160e8 | ||
|
|
235e48a843 | ||
|
|
93fc5dc6ae | ||
|
|
5c49902ffb | ||
|
|
c9c148e050 | ||
|
|
adf9de3c63 | ||
|
|
26612e371d | ||
|
|
b194506943 | ||
|
|
79dbffff1d | ||
|
|
0359934bb9 |
No files matched your search
@@ -0,0 +1,8 @@
|
||||
.navds-accordion-anchor-menu {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.navds-accordion-anchor-menu__title {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
@@ -1,113 +1,85 @@
|
||||
/*******************
|
||||
* .accordion-menu *
|
||||
*******************/
|
||||
|
||||
.navds-accordion-menu__html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__nav {
|
||||
padding: var(--navds-layout-padding-small) 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__container {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.navds-accordion-menu__list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__title {
|
||||
margin: var(--navds-layout-padding-small) 0;
|
||||
padding: 0 var(--navds-layout-padding-small);
|
||||
}
|
||||
|
||||
.navds-accordion-menu__item {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: var(--navds-font-weight-bold);
|
||||
}
|
||||
|
||||
.navds-accordion-menu__item--active::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 0.5rem;
|
||||
height: 100%;
|
||||
background-color: var(--navds-color-blue-50);
|
||||
}
|
||||
|
||||
.navds-link.navds-accordion-menu__link,
|
||||
.navds-link.navds-accordion-menu__button {
|
||||
min-height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 var(--navds-layout-padding-small);
|
||||
background: none;
|
||||
.navds-accordion-menu-collapsable__button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navds-link.navds-accordion-menu__link--active:not(:active):not(:focus) {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--navds-color-text-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__chevron {
|
||||
margin-right: 0.25rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__button {
|
||||
display: flex;
|
||||
clear: both;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font: inherit;
|
||||
text-align: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: var(--navds-font-weight-bold);
|
||||
color: var(--navds-color-text-link);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__dropdown--content .navds-accordion-menu__link,
|
||||
.navds-accordion-menu__dropdown--content .navds-accordion-menu__button {
|
||||
padding-left: calc(var(--navds-layout-padding-small) + 2rem);
|
||||
.navds-accordion-menu-collapsable .navds-accordion-menu__list,
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable__button {
|
||||
font-weight: var(--navds-font-weight-regular);
|
||||
}
|
||||
|
||||
@media (min-width: 448px) {
|
||||
.navds-accordion-menu__nav {
|
||||
padding: var(--navds-layout-padding-medium-and-larger) 0;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__title {
|
||||
padding: 0 var(--navds-layout-padding-medium-and-larger);
|
||||
}
|
||||
|
||||
.navds-link.navds-accordion-menu__link,
|
||||
.navds-link.navds-accordion-menu__button {
|
||||
padding: 0 var(--navds-layout-padding-medium-and-larger);
|
||||
}
|
||||
|
||||
.navds-accordion-menu__dropdown--content .navds-accordion-menu__link,
|
||||
.navds-accordion-menu__dropdown--content .navds-accordion-menu__button {
|
||||
padding: 0 calc(var(--navds-layout-padding-medium-and-larger) + 2rem);
|
||||
}
|
||||
.navds-accordion-menu-item,
|
||||
.navds-accordion-menu-collapsable__button {
|
||||
display: flex;
|
||||
padding: 0.75rem 1rem;
|
||||
line-height: 1.5rem; /* TODO: fix typography. */
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.navds-accordion-menu__nav {
|
||||
padding: var(--navds-layout-padding-small) 0;
|
||||
}
|
||||
.navds-accordion-menu-collapsable__expand-icon {
|
||||
font-size: 1.5rem;
|
||||
color: var(--navds-color-text-link);
|
||||
flex-shrink: 0;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__title {
|
||||
padding: 0 var(--navds-layout-padding-small);
|
||||
}
|
||||
.navds-accordion-menu-collapsable--open
|
||||
> .navds-accordion-menu-collapsable__button
|
||||
> .navds-accordion-menu-collapsable__expand-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.navds-link.navds-accordion-menu__link,
|
||||
.navds-link.navds-accordion-menu__button {
|
||||
padding: 0 calc(var(--navds-layout-padding-small));
|
||||
}
|
||||
.navds-accordion-menu-collapsable .navds-accordion-menu-item,
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable__button {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.navds-accordion-menu__dropdown--content .navds-accordion-menu__link,
|
||||
.navds-accordion-menu__dropdown--content .navds-accordion-menu__button {
|
||||
padding: 0 calc(var(--navds-layout-padding-small) + 1rem);
|
||||
}
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-item,
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable__button {
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
.navds-accordion-menu-item:focus,
|
||||
.navds-accordion-menu-collapsable__button:focus {
|
||||
outline: 3px solid var(--navds-text-focus);
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
.navds-accordion-menu-item:hover,
|
||||
.navds-accordion-menu-collapsable__button:hover {
|
||||
background-color: var(--navds-color-gray-20);
|
||||
}
|
||||
|
||||
.navds-accordion-menu-item--active {
|
||||
box-shadow: inset 0.5rem 0 var(--navds-color-text-link);
|
||||
color: var(--navds-color-text);
|
||||
text-decoration: none;
|
||||
font-weight: var(--navds-font-weight-bold);
|
||||
}
|
||||
@@ -6,6 +6,10 @@
|
||||
@import "print.css";
|
||||
@import "utility.css";
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--navds-font-family);
|
||||
line-height: 1.375;
|
||||
@@ -16,3 +20,16 @@ body {
|
||||
a {
|
||||
color: var(--navds-color-text-link);
|
||||
}
|
||||
|
||||
/* https://web.dev/prefers-reduced-motion/ */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, ::before, ::after {
|
||||
animation-delay: -1ms !important;
|
||||
animation-duration: 1ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
background-attachment: initial !important;
|
||||
scroll-behavior: auto !important;
|
||||
transition-duration: 0s !important;
|
||||
transition-delay: 0s !important;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
@import "baseline/baseline.css";
|
||||
@import "accordion.css";
|
||||
@import "accordion-anchor-menu.css";
|
||||
@import "accordion-menu.css";
|
||||
@import "alert.css";
|
||||
@import "button.css";
|
||||
|
||||
@@ -265,6 +265,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 448px) and (max-width: 959px) {
|
||||
.navds-layout__section .navds-accordion-anchor-menu__title {
|
||||
margin-left: 2.5rem;
|
||||
margin-right: 2.5rem;
|
||||
}
|
||||
.navds-layout__section .navds-accordion-menu-item,
|
||||
.navds-layout__section
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable__button {
|
||||
padding-left: 2.5rem;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.navds-layout__section
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-item,
|
||||
.navds-layout__section
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable__button {
|
||||
padding-left: 3.5rem;
|
||||
padding-right: 3.5rem;
|
||||
}
|
||||
|
||||
.navds-layout__section
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-item,
|
||||
.navds-layout__section
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable
|
||||
.navds-accordion-menu-collapsable__button {
|
||||
padding-left: 4.5rem;
|
||||
padding-right: 4.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 648px) {
|
||||
/* prettier-ignore */
|
||||
.navds-layout__container--2-columns .navds-layout__panel--icon-margin:first-child {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.5",
|
||||
"private": false,
|
||||
"description": "Css for NAV Designsystem components",
|
||||
"author": "NAV Designsystem team",
|
||||
@@ -21,7 +21,7 @@
|
||||
"watch-css": "postcss -w --use postcss-import -o dist/index.css index.css"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^0.1.1",
|
||||
"@navikt/ds-tokens": "^0.1.2",
|
||||
"normalize.css": "^8.0.1",
|
||||
"postcss": "^8.2.7",
|
||||
"postcss-cli": "^8.3.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-icons",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"private": false,
|
||||
"description": "NAVs egne ikoner i SVG og React format",
|
||||
"repository": {
|
||||
@@ -22,6 +22,7 @@
|
||||
"files": [
|
||||
"/cjs",
|
||||
"/esm",
|
||||
"/src",
|
||||
"/svg"
|
||||
],
|
||||
"scripts": {
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import React, { createContext, useCallback, useContext, useState } from "react";
|
||||
interface Props {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
}
|
||||
|
||||
export interface ActiveAnchorStore {
|
||||
anchors: string[];
|
||||
activeAnchor?: string;
|
||||
registerAnchor: (anchor: string) => void;
|
||||
unregisterAnchor: (anchor: string) => void;
|
||||
setActiveAnchor: (anchor: string) => void;
|
||||
}
|
||||
|
||||
export const ActiveAnchorContext = createContext({} as ActiveAnchorStore);
|
||||
export const ActiveAnchorProvider = (props: Props) => {
|
||||
const { children } = props;
|
||||
const [anchors, setAnchors] = useState<string[]>([]);
|
||||
const [activeAnchor, setActiveAnchor] = useState<string>();
|
||||
|
||||
return (
|
||||
<ActiveAnchorContext.Provider
|
||||
value={{
|
||||
anchors,
|
||||
activeAnchor,
|
||||
registerAnchor: useCallback((anchor) => {
|
||||
setAnchors((anchors) => [...anchors, anchor]);
|
||||
}, []),
|
||||
unregisterAnchor: useCallback((anchor) => {
|
||||
setAnchors((anchors) => anchors.filter((a) => a !== anchor));
|
||||
}, []),
|
||||
setActiveAnchor: useCallback((anchor) => {
|
||||
setActiveAnchor(anchor);
|
||||
}, []),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActiveAnchorContext.Provider>
|
||||
);
|
||||
};
|
||||
export const useStore = () => useContext(ActiveAnchorContext);
|
||||
@@ -1,52 +0,0 @@
|
||||
import React, { forwardRef, HTMLAttributes, useState } from "react";
|
||||
import cl from "classnames";
|
||||
import { Expand } from "@navikt/ds-icons";
|
||||
|
||||
export interface AccordionMenuCollapsableProps
|
||||
extends HTMLAttributes<HTMLButtonElement> {
|
||||
title: string;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
const Collapsable = forwardRef<
|
||||
HTMLButtonElement,
|
||||
AccordionMenuCollapsableProps
|
||||
>(({ children, active, title, className, ...rest }, ref) => {
|
||||
const [open, setOpen] = useState(active || false);
|
||||
return (
|
||||
<li
|
||||
className={cl(
|
||||
"navds-accordion-menu__item",
|
||||
"navds-accordion-menu__dropdown"
|
||||
)}
|
||||
>
|
||||
<button
|
||||
ref={ref}
|
||||
onClick={() => setOpen(!open)}
|
||||
className={cl("navds-link", "navds-accordion-menu__button", className)}
|
||||
{...rest}
|
||||
>
|
||||
{title}
|
||||
<Expand
|
||||
className={cl(
|
||||
"navds-accordion__chevron",
|
||||
`navds-accordion__chevron--${open ? "up" : "down"}`,
|
||||
`navds-accordion-menu__chevron`
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
{open && (
|
||||
<ul
|
||||
className={cl(
|
||||
"navds-accordion-menu__container",
|
||||
"navds-accordion-menu__dropdown--content"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
export default Collapsable;
|
||||
@@ -1,62 +0,0 @@
|
||||
import React, { forwardRef, useEffect } from "react";
|
||||
import { useStore } from "./ActiveAnchorStore";
|
||||
import { OverridableComponent } from "../util";
|
||||
import cl from "classnames";
|
||||
|
||||
export interface AccordionMenuItemProps {
|
||||
props: {
|
||||
children: React.ReactNode;
|
||||
active?: boolean;
|
||||
} & React.HTMLAttributes<HTMLLIElement>;
|
||||
defaultComponent: "a";
|
||||
}
|
||||
|
||||
const Item: OverridableComponent<AccordionMenuItemProps> = forwardRef(
|
||||
(
|
||||
{
|
||||
children,
|
||||
component: Component = "a",
|
||||
active = false,
|
||||
className,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const anchor = rest.href && rest.href.split("#")[1];
|
||||
const { activeAnchor, registerAnchor, unregisterAnchor } = useStore();
|
||||
const isActive = active || (anchor && activeAnchor === anchor) || false;
|
||||
|
||||
useEffect(() => {
|
||||
if (anchor) {
|
||||
const target = document.getElementById(anchor);
|
||||
if (target) {
|
||||
registerAnchor(anchor);
|
||||
return () => {
|
||||
unregisterAnchor(anchor);
|
||||
};
|
||||
}
|
||||
}
|
||||
}, [anchor, registerAnchor, unregisterAnchor]);
|
||||
|
||||
return (
|
||||
<li
|
||||
className={cl(
|
||||
"navds-accordion-menu__item",
|
||||
isActive && "navds-accordion-menu__item--active"
|
||||
)}
|
||||
>
|
||||
<Component
|
||||
ref={ref}
|
||||
className={cl("navds-link", "navds-accordion-menu__link", className, {
|
||||
"navds-accordion-menu__link--active": isActive,
|
||||
})}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default Item;
|
||||
@@ -1,68 +0,0 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
ForwardRefExoticComponent,
|
||||
HTMLAttributes,
|
||||
useEffect,
|
||||
} from "react";
|
||||
import { default as Item, AccordionMenuItemProps } from "./Item";
|
||||
import { default as Collapsable } from "./Collapsable";
|
||||
import { AccordionMenuCollapsableProps } from "./Collapsable";
|
||||
import { Heading, OverridableComponent } from "../index";
|
||||
import { ActiveAnchorProvider } from "./ActiveAnchorStore";
|
||||
import Nav from "./Nav";
|
||||
import cl from "classnames";
|
||||
|
||||
export interface LayoutWithSubComponents
|
||||
extends ForwardRefExoticComponent<AccordionMenuProps> {
|
||||
Collapsable: ForwardRefExoticComponent<AccordionMenuCollapsableProps>;
|
||||
Item: OverridableComponent<AccordionMenuItemProps>;
|
||||
}
|
||||
|
||||
export interface AccordionMenuProps extends HTMLAttributes<HTMLUListElement> {
|
||||
title?: string;
|
||||
smoothScrollBehavior?: boolean;
|
||||
}
|
||||
|
||||
const AccordionMenu = forwardRef<HTMLUListElement, AccordionMenuProps>(
|
||||
(
|
||||
{ children, title, smoothScrollBehavior = true, className, ...rest },
|
||||
ref
|
||||
) => {
|
||||
useEffect(() => {
|
||||
if (smoothScrollBehavior) {
|
||||
const htmlElement = document.documentElement;
|
||||
htmlElement.classList.add("navds-accordion-menu__html");
|
||||
return () => {
|
||||
htmlElement.classList.remove("navds-accordion-menu__html");
|
||||
};
|
||||
}
|
||||
}, [smoothScrollBehavior]);
|
||||
|
||||
return (
|
||||
<ActiveAnchorProvider>
|
||||
<Nav title={title}>
|
||||
{title && (
|
||||
<Heading
|
||||
level={2}
|
||||
size="medium"
|
||||
className={cl("navds-accordion-menu__title", className)}
|
||||
>
|
||||
{title}
|
||||
</Heading>
|
||||
)}
|
||||
<ul
|
||||
ref={ref}
|
||||
className={cl("navds-accordion-menu__container", className)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
</Nav>
|
||||
</ActiveAnchorProvider>
|
||||
);
|
||||
}
|
||||
) as LayoutWithSubComponents;
|
||||
|
||||
AccordionMenu.Item = Item;
|
||||
AccordionMenu.Collapsable = Collapsable;
|
||||
export default AccordionMenu;
|
||||
@@ -1,54 +0,0 @@
|
||||
import React, { forwardRef, HTMLAttributes, useEffect } from "react";
|
||||
import { useStore } from "./ActiveAnchorStore";
|
||||
|
||||
export interface AccordionMenuItemProps
|
||||
extends HTMLAttributes<HTMLAnchorElement> {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const Nav = forwardRef<HTMLAnchorElement, AccordionMenuItemProps>(
|
||||
({ children, title, className, ...rest }, ref) => {
|
||||
const { anchors, activeAnchor, setActiveAnchor } = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
const scrollListener = () => {
|
||||
const offset = 100;
|
||||
const lastPassedAnchor = anchors
|
||||
.map((anchor) => document.getElementById(anchor))
|
||||
.map((element: HTMLElement) => ({
|
||||
id: element.id,
|
||||
top: element.getBoundingClientRect().top - offset,
|
||||
scrolledToBottom:
|
||||
window.innerHeight + window.pageYOffset >=
|
||||
document.body.offsetHeight,
|
||||
}))
|
||||
.filter((element) => element.scrolledToBottom || element.top <= 0)
|
||||
.sort((a, b) => (a.top < b.top ? -1 : 1))
|
||||
.map((anchor) => anchor.id)
|
||||
.pop();
|
||||
|
||||
// Set active anchor and related url hash
|
||||
if (lastPassedAnchor && activeAnchor !== lastPassedAnchor) {
|
||||
const { href, hash } = window.location;
|
||||
const urlWithoutHash = href.replace(hash, "");
|
||||
const urlWithAnchor = `${urlWithoutHash}#${lastPassedAnchor}`;
|
||||
const title = document.title;
|
||||
window.history.pushState(lastPassedAnchor, title, urlWithAnchor);
|
||||
setActiveAnchor(lastPassedAnchor);
|
||||
}
|
||||
};
|
||||
window.addEventListener("scroll", scrollListener);
|
||||
return () => {
|
||||
window.removeEventListener("scroll", scrollListener);
|
||||
};
|
||||
}, [anchors, activeAnchor, setActiveAnchor]);
|
||||
|
||||
return (
|
||||
<nav className={"navds-accordion-menu__nav"} aria-label={title || "Meny"}>
|
||||
{children}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default Nav;
|
||||
@@ -1,5 +0,0 @@
|
||||
import { default as AccordionMenu, AccordionMenuProps } from "./Menu";
|
||||
import { AccordionMenuItemProps } from "./Item";
|
||||
|
||||
export { AccordionMenu, AccordionMenuProps, AccordionMenuItemProps };
|
||||
export default AccordionMenu;
|
||||
@@ -1,12 +0,0 @@
|
||||
.navds-story-menu {
|
||||
margin-top: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.navds-story-menu li:not(:last-child) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.navds-story-section {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "0.2.4",
|
||||
"version": "0.2.8",
|
||||
"private": false,
|
||||
"description": "NAV designsystem react components",
|
||||
"author": "NAV Designsystem team",
|
||||
@@ -24,15 +24,17 @@
|
||||
},
|
||||
"files": [
|
||||
"/cjs",
|
||||
"/esm"
|
||||
"/esm",
|
||||
"/src"
|
||||
],
|
||||
"scripts": {
|
||||
"cjs-package": "copyfiles -f config/package.json cjs",
|
||||
"build": "tsc -p tsconfig.json && tsc -p tsconfig-esm.json && yarn cjs-package",
|
||||
"clean": "rm -rf cjs esm",
|
||||
"build": "yarn run clean && tsc && tsc -p tsconfig-esm.json && yarn cjs-package",
|
||||
"watch": "tsc --watch -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navikt/ds-icons": "^0.2.0",
|
||||
"@navikt/ds-icons": "^0.2.1",
|
||||
"@popperjs/core": "^2.5.4",
|
||||
"classnames": "^2.2.6",
|
||||
"react-collapse": "^5.1.0",
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import { AccordionMenu as BaseAccordionMenu } from "../accordion-menu";
|
||||
import { Heading } from "../index";
|
||||
import { ActiveAnchorProvider } from "./ActiveAnchorStore";
|
||||
import cl from "classnames";
|
||||
|
||||
export interface AccordionAnchorMenuProps
|
||||
extends HTMLAttributes<HTMLDivElement> {
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AccordionAnchorMenu = forwardRef<
|
||||
HTMLDivElement,
|
||||
AccordionAnchorMenuProps
|
||||
>(({ children, title, className, ...rest }, ref) => (
|
||||
<div
|
||||
className={cl("navds-accordion-anchor-menu", className)}
|
||||
ref={ref}
|
||||
{...rest}
|
||||
>
|
||||
{title && (
|
||||
<Heading
|
||||
level={2}
|
||||
size="medium"
|
||||
className="navds-accordion-anchor-menu__title"
|
||||
>
|
||||
{title}
|
||||
</Heading>
|
||||
)}
|
||||
<ActiveAnchorProvider>
|
||||
<BaseAccordionMenu aria-label={title || "Meny"}>
|
||||
{children}
|
||||
</BaseAccordionMenu>
|
||||
</ActiveAnchorProvider>
|
||||
</div>
|
||||
));
|
||||
|
||||
export default AccordionAnchorMenu;
|
||||
@@ -0,0 +1 @@
|
||||
export { AccordionMenuCollapsable as AccordionAnchorMenuCollapsable } from "../accordion-menu";
|
||||
@@ -0,0 +1,39 @@
|
||||
import React, { forwardRef, useEffect } from "react";
|
||||
import { AccordionMenuItem, AccordionMenuItemType } from "../accordion-menu";
|
||||
import { useStore } from "./ActiveAnchorStore";
|
||||
|
||||
const AccordionAnchorMenuItem: AccordionMenuItemType = forwardRef(
|
||||
(props, ref) => {
|
||||
const isAnchorActive = useIsAnchorActive(
|
||||
props.href && props.href.split("#")[1]
|
||||
);
|
||||
|
||||
return (
|
||||
<AccordionMenuItem
|
||||
{...props}
|
||||
ref={ref}
|
||||
active={props.active || isAnchorActive}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const useIsAnchorActive = (anchor: string | undefined) => {
|
||||
const { activeAnchor, registerAnchor, unregisterAnchor } = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (anchor) {
|
||||
const target = document.getElementById(anchor);
|
||||
if (target) {
|
||||
registerAnchor(anchor);
|
||||
return () => {
|
||||
unregisterAnchor(anchor);
|
||||
};
|
||||
}
|
||||
}
|
||||
}, [anchor, registerAnchor, unregisterAnchor]);
|
||||
|
||||
return (anchor && activeAnchor === anchor) || false;
|
||||
};
|
||||
|
||||
export default AccordionAnchorMenuItem;
|
||||
@@ -0,0 +1,69 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useState,
|
||||
useEffect,
|
||||
} from "react";
|
||||
|
||||
export interface ActiveAnchorStore {
|
||||
activeAnchor?: string;
|
||||
registerAnchor: (anchor: string) => void;
|
||||
unregisterAnchor: (anchor: string) => void;
|
||||
}
|
||||
|
||||
export const ActiveAnchorContext = createContext({} as ActiveAnchorStore);
|
||||
export const ActiveAnchorProvider = ({ children }) => {
|
||||
const [anchors, setAnchors] = useState<string[]>([]);
|
||||
const [activeAnchor, setActiveAnchor] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
const scrollListener = () => {
|
||||
const offset = 100;
|
||||
const lastPassedAnchor = anchors
|
||||
.map((anchor) => document.getElementById(anchor))
|
||||
.map((element: HTMLElement) => ({
|
||||
id: element.id,
|
||||
top: element.getBoundingClientRect().top - offset,
|
||||
scrolledToBottom:
|
||||
window.innerHeight + window.pageYOffset >=
|
||||
document.body.offsetHeight,
|
||||
}))
|
||||
.filter((element) => element.scrolledToBottom || element.top <= 0)
|
||||
.sort((a, b) => (a.top < b.top ? -1 : 1))
|
||||
.map((anchor) => anchor.id)
|
||||
.pop();
|
||||
|
||||
// Set active anchor and related url hash
|
||||
if (lastPassedAnchor && activeAnchor !== lastPassedAnchor) {
|
||||
const { href, hash } = window.location;
|
||||
const urlWithoutHash = href.replace(hash, "");
|
||||
const urlWithAnchor = `${urlWithoutHash}#${lastPassedAnchor}`;
|
||||
const title = document.title;
|
||||
window.history.pushState(lastPassedAnchor, title, urlWithAnchor);
|
||||
setActiveAnchor(lastPassedAnchor);
|
||||
}
|
||||
};
|
||||
window.addEventListener("scroll", scrollListener);
|
||||
return () => {
|
||||
window.removeEventListener("scroll", scrollListener);
|
||||
};
|
||||
}, [anchors, activeAnchor]);
|
||||
|
||||
return (
|
||||
<ActiveAnchorContext.Provider
|
||||
value={{
|
||||
activeAnchor,
|
||||
registerAnchor: useCallback((anchor) => {
|
||||
setAnchors((anchors) => [...anchors, anchor]);
|
||||
}, []),
|
||||
unregisterAnchor: useCallback((anchor) => {
|
||||
setAnchors((anchors) => anchors.filter((a) => a !== anchor));
|
||||
}, []),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActiveAnchorContext.Provider>
|
||||
);
|
||||
};
|
||||
export const useStore = () => useContext(ActiveAnchorContext);
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as AccordionAnchorMenu } from "./AccordionAnchorMenu";
|
||||
export { AccordionAnchorMenuCollapsable } from "./AccordionAnchorMenuCollapsable";
|
||||
export { default as AccordionAnchorMenuItem } from "./AccordionAnchorMenuItem";
|
||||
+29
-27
@@ -1,5 +1,9 @@
|
||||
import * as React from "react";
|
||||
import Menu from "../index";
|
||||
import {
|
||||
AccordionAnchorMenu as Menu,
|
||||
AccordionAnchorMenuCollapsable as Collapsable,
|
||||
AccordionAnchorMenuItem as Item,
|
||||
} from "../index";
|
||||
import { useEffect } from "react";
|
||||
import { ProductPageLayout } from "../../index";
|
||||
import { Right } from "../../layouts/stories/components/sections/Right";
|
||||
@@ -9,10 +13,8 @@ import { MainTwo } from "../../layouts/stories/components/sections/Main";
|
||||
import { MainThree } from "../../layouts/stories/components/sections/Main";
|
||||
import { Link, HashRouter as Router } from "react-router-dom";
|
||||
|
||||
import "./components/styles.css";
|
||||
|
||||
export default {
|
||||
title: "ds-react/accordion-menu",
|
||||
title: "ds-react/accordion-anchor-menu",
|
||||
component: { Menu },
|
||||
decorators: [
|
||||
(Story) => {
|
||||
@@ -35,13 +37,13 @@ export default {
|
||||
export const All = () => (
|
||||
<ProductPageLayout title={"Nunc et lorem"}>
|
||||
<ProductPageLayout.Section left sticky withPadding={false}>
|
||||
<Menu title={"Minima"}>
|
||||
<Menu.Item href={"#leo-quis"}>Leo quis</Menu.Item>
|
||||
<Menu.Collapsable title={"Proin accumsan"}>
|
||||
<Menu.Item href={"#nulla-pariatur"}>Nulla pariatur</Menu.Item>
|
||||
<Menu.Item href={"#luctus-justo"}>Luctus justo</Menu.Item>
|
||||
</Menu.Collapsable>
|
||||
<Menu.Item href={"#maecenas-in-pretium"}>Maecenas in pretium</Menu.Item>
|
||||
<Menu title={"Minima"} smoothScrollBehavior>
|
||||
<Item href={"#leo-quis"}>Leo quis</Item>
|
||||
<Collapsable title={"Proin accumsan"}>
|
||||
<Item href={"#nulla-pariatur"}>Nulla pariatur</Item>
|
||||
<Item href={"#luctus-justo"}>Luctus justo</Item>
|
||||
</Collapsable>
|
||||
<Item href={"#maecenas-in-pretium"}>Maecenas in pretium</Item>
|
||||
</Menu>
|
||||
</ProductPageLayout.Section>
|
||||
<ProductPageLayout.Section whiteBackground={false} withPadding={false}>
|
||||
@@ -77,28 +79,28 @@ export const ReactRouter = () => (
|
||||
<ProductPageLayout title={"Nunc et lorem"}>
|
||||
<ProductPageLayout.Section left sticky withPadding={false}>
|
||||
<Menu>
|
||||
<Menu.Item component={Link} to="/link1">
|
||||
<Item component={Link} to="/link1">
|
||||
Leo quis
|
||||
</Menu.Item>
|
||||
<Menu.Collapsable title={"Proin accumsan"}>
|
||||
<Menu.Item component={Link} to="/link2">
|
||||
</Item>
|
||||
<Collapsable title={"Proin accumsan"}>
|
||||
<Item component={Link} to="/link2">
|
||||
Nulla pariatur
|
||||
</Menu.Item>
|
||||
<Menu.Item component={Link} to="/link3">
|
||||
</Item>
|
||||
<Item component={Link} to="/link3">
|
||||
Luctus justo
|
||||
</Menu.Item>
|
||||
</Menu.Collapsable>
|
||||
<Menu.Collapsable title={"Sint cupidatat"}>
|
||||
<Menu.Item component={Link} to="/link4">
|
||||
</Item>
|
||||
</Collapsable>
|
||||
<Collapsable title={"Sint cupidatat"}>
|
||||
<Item component={Link} to="/link4">
|
||||
Nulla pariatur
|
||||
</Menu.Item>
|
||||
<Menu.Item component={Link} to="/link5">
|
||||
</Item>
|
||||
<Item component={Link} to="/link5">
|
||||
Luctus justo
|
||||
</Menu.Item>
|
||||
</Menu.Collapsable>
|
||||
<Menu.Item component={Link} to="/link6">
|
||||
</Item>
|
||||
</Collapsable>
|
||||
<Item component={Link} to="/link6">
|
||||
Maecenas in pretium
|
||||
</Menu.Item>
|
||||
</Item>
|
||||
</Menu>
|
||||
</ProductPageLayout.Section>
|
||||
<ProductPageLayout.Section whiteBackground={false} withPadding={false}>
|
||||
@@ -0,0 +1,25 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import MenuItems from "./MenuItems";
|
||||
|
||||
export interface AccordionMenuProps extends HTMLAttributes<HTMLElement> {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AccordionMenu = forwardRef<HTMLElement, AccordionMenuProps>(
|
||||
({ children, className, ...rest }, ref) => {
|
||||
return (
|
||||
<nav
|
||||
role="navigation"
|
||||
ref={ref}
|
||||
className={cl("navds-accordion-menu", className)}
|
||||
{...rest}
|
||||
>
|
||||
<MenuItems>{children}</MenuItems>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default AccordionMenu;
|
||||
@@ -0,0 +1,44 @@
|
||||
import React, { forwardRef, HTMLAttributes, useState } from "react";
|
||||
import cl from "classnames";
|
||||
import { Expand } from "@navikt/ds-icons";
|
||||
import MenuItems from "./MenuItems";
|
||||
|
||||
export interface AccordionMenuCollapsableProps
|
||||
extends HTMLAttributes<HTMLDivElement> {
|
||||
title: string;
|
||||
defaultOpen?: boolean;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AccordionMenuCollapsable = forwardRef<
|
||||
HTMLDivElement,
|
||||
AccordionMenuCollapsableProps
|
||||
>(({ children, defaultOpen = false, title, className, ...rest }, ref) => {
|
||||
const [isOpen, setIsOpen] = useState(defaultOpen);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cl("navds-accordion-menu-collapsable", className, {
|
||||
"navds-accordion-menu-collapsable--open": isOpen,
|
||||
})}
|
||||
{...rest}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="navds-accordion-menu-collapsable__button"
|
||||
>
|
||||
{title}
|
||||
<Expand
|
||||
role="img"
|
||||
aria-label={isOpen ? "Pil peker opp" : "Pil peker ned"}
|
||||
className="navds-accordion-menu-collapsable__expand-icon"
|
||||
/>
|
||||
</button>
|
||||
{isOpen && <MenuItems>{children}</MenuItems>}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default AccordionMenuCollapsable;
|
||||
@@ -0,0 +1,40 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { OverridableComponent } from "../util";
|
||||
import cl from "classnames";
|
||||
|
||||
export type AccordionMenuItemType = OverridableComponent<AccordionMenuItemProps>;
|
||||
|
||||
export interface AccordionMenuItemProps {
|
||||
props: {
|
||||
children: React.ReactNode;
|
||||
active?: boolean;
|
||||
} & React.HTMLAttributes<HTMLAnchorElement>;
|
||||
defaultComponent: "a";
|
||||
}
|
||||
|
||||
const AccordionMenuItem: AccordionMenuItemType = forwardRef(
|
||||
(
|
||||
{
|
||||
children,
|
||||
component: Component = "a",
|
||||
active = false,
|
||||
className,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<Component
|
||||
ref={ref}
|
||||
className={cl("navds-accordion-menu-item", className, {
|
||||
"navds-accordion-menu-item--active": active,
|
||||
})}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default AccordionMenuItem;
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
const MenuItems = ({ children }) => (
|
||||
<ul className="navds-accordion-menu__list">
|
||||
{React.Children.toArray(children).map((child, i) => (
|
||||
<li key={i} className="navds-accordion-menu__list-item">
|
||||
{child}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
||||
export default MenuItems;
|
||||
@@ -0,0 +1,6 @@
|
||||
export { default as AccordionMenu } from "./AccordionMenu";
|
||||
export { default as AccordionMenuCollapsable } from "./AccordionMenuCollapsable";
|
||||
export {
|
||||
default as AccordionMenuItem,
|
||||
AccordionMenuItemType,
|
||||
} from "./AccordionMenuItem";
|
||||
@@ -0,0 +1,160 @@
|
||||
import * as React from "react";
|
||||
import {
|
||||
AccordionMenu,
|
||||
AccordionMenuItem as Item,
|
||||
AccordionMenuCollapsable as Collapsable,
|
||||
} from "../index";
|
||||
import { NavLink, HashRouter as Router } from "react-router-dom";
|
||||
|
||||
export default {
|
||||
title: "ds-react/accordion-menu",
|
||||
component: { AccordionMenu },
|
||||
parameters: {
|
||||
backgrounds: {
|
||||
default: "light",
|
||||
values: [
|
||||
{
|
||||
name: "light",
|
||||
value: "#F1F1F1",
|
||||
},
|
||||
{
|
||||
name: "white",
|
||||
value: "#fff",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const All = () => (
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
maxWidth: 288,
|
||||
}}
|
||||
>
|
||||
<AccordionMenu>
|
||||
<Item href="#leo">Leo</Item>
|
||||
<Collapsable title="Proin">
|
||||
<Item href="#nulla" active>
|
||||
Nulla
|
||||
</Item>
|
||||
<Item href="#luctus">Luctus</Item>
|
||||
</Collapsable>
|
||||
<Collapsable title="Accumsan">
|
||||
<Item href="#justo" ariaCurrent="page">
|
||||
Justo
|
||||
</Item>
|
||||
<Item href="#pariatur" active ariaCurrent="page">
|
||||
Pariatur
|
||||
</Item>
|
||||
<Collapsable title="Proin">
|
||||
<Item href="#nulla" active>
|
||||
Nulla
|
||||
</Item>
|
||||
<Item href="#luctus">Luctus</Item>
|
||||
</Collapsable>
|
||||
</Collapsable>
|
||||
<Item href="#justo" active>
|
||||
Justo
|
||||
</Item>
|
||||
<Item href="#pariatur">Pariatur</Item>
|
||||
</AccordionMenu>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const ReactRouter = () => (
|
||||
<AccordionMenu>
|
||||
<Collapsable title="Nivå 1 – 3 rems høy">
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/1"
|
||||
>
|
||||
Nivå 2 innrykk 2 rems
|
||||
</Item>
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/2"
|
||||
>
|
||||
Subtitle 3
|
||||
</Item>
|
||||
</Collapsable>
|
||||
<Collapsable title="Skjema og søknad">
|
||||
<Collapsable title="Satser">
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/3"
|
||||
>
|
||||
Nivå 3 – 3 rems innrykk Har du rett til dagpenger når du mottar annen
|
||||
økonomisk støtte fra NAV?
|
||||
</Item>
|
||||
</Collapsable>
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/4"
|
||||
>
|
||||
Subtitle 2
|
||||
</Item>
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/5"
|
||||
>
|
||||
Subtitle 3
|
||||
</Item>
|
||||
</Collapsable>
|
||||
<Collapsable title="Sakbehandlingstider">
|
||||
<Collapsable title="Subtitle 4 Lang tittel på andre nivå lorem ipsum">
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/6"
|
||||
>
|
||||
Subtitle 5
|
||||
</Item>
|
||||
</Collapsable>
|
||||
</Collapsable>
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/7"
|
||||
>
|
||||
Internasjonalt
|
||||
</Item>
|
||||
<Collapsable title="Meld fra om endringer lang tittel på første nivå">
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/8"
|
||||
>
|
||||
Subtitle 6
|
||||
</Item>
|
||||
</Collapsable>
|
||||
<Item
|
||||
component={NavLink}
|
||||
activeClassName="navds-accordion-menu-item--active"
|
||||
to="/9"
|
||||
>
|
||||
Nivå 1
|
||||
</Item>
|
||||
</AccordionMenu>
|
||||
);
|
||||
|
||||
ReactRouter.decorators = [
|
||||
(Story) => (
|
||||
<Router>
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
maxWidth: 288,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</div>
|
||||
</Router>
|
||||
),
|
||||
];
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -20,6 +20,11 @@ export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
* @default "medium"
|
||||
*/
|
||||
size?: "medium" | "small";
|
||||
/**
|
||||
* Prevent the user from interacting with the button: it cannot be pressed or focused.
|
||||
* @default false
|
||||
*/
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,3 +1,13 @@
|
||||
export {
|
||||
AccordionAnchorMenu,
|
||||
AccordionAnchorMenuCollapsable,
|
||||
AccordionAnchorMenuItem,
|
||||
} from "./accordion-anchor-menu";
|
||||
export {
|
||||
AccordionMenu,
|
||||
AccordionMenuCollapsable,
|
||||
AccordionMenuItem,
|
||||
} from "./accordion-menu";
|
||||
export { Accordion, AccordionProps } from "./accordion/index";
|
||||
export { Alert, AlertProps } from "./alert/index";
|
||||
export { Button, ButtonProps } from "./button/index";
|
||||
@@ -19,11 +29,6 @@ export {
|
||||
ProductPageLayoutProps,
|
||||
ProductPageLayoutSectionProps,
|
||||
} from "./layouts/index";
|
||||
export {
|
||||
AccordionMenu,
|
||||
AccordionMenuProps,
|
||||
AccordionMenuItemProps,
|
||||
} from "./accordion-menu";
|
||||
export { Link, LinkProps } from "./link/index";
|
||||
export { Modal, ModalProps } from "./modal/index";
|
||||
export { Popover, PopoverProps } from "./popover/index";
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
-5
@@ -7,7 +7,6 @@ import {
|
||||
Link,
|
||||
Paragraph,
|
||||
} from "../../../../index";
|
||||
import Snakkeboble from "nav-frontend-snakkeboble";
|
||||
|
||||
export const Main = ({ title = true }) => (
|
||||
<>
|
||||
@@ -121,16 +120,12 @@ export const MainThree = ({ title = true }) => (
|
||||
Quisque varius ornare nulla, ut condimentum sapien dapibus non. Nullam
|
||||
eget interdum nibh
|
||||
</Paragraph>
|
||||
<Snakkeboble>Integer aliquam feugiat purus et imperdie</Snakkeboble>
|
||||
<Paragraph>
|
||||
Cras tempor eros sed dapibus egestas. Curabitur bibendum quam quis lectus
|
||||
pretium lacinia. Nunc consequat ac augue quis laoreet. Donec porta, erat
|
||||
vitae blandit egestas, arcu metus pharetra nulla, et laoreet risus purus
|
||||
convallis turpis.
|
||||
</Paragraph>
|
||||
<Snakkeboble pilHoyre={true}>
|
||||
Integer aliquam feugiat purus et imperdie
|
||||
</Snakkeboble>
|
||||
<Paragraph>
|
||||
Suspendisse potenti. Praesent at elit interdum, porttitor sem eget,
|
||||
blandit dolor. Nunc eget consectetur felis, id scelerisque sapien. Ut id
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -2,12 +2,13 @@
|
||||
"extends": "../../utilities/build/tsconfig-pakker.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./esm",
|
||||
"rootDir": "./",
|
||||
"rootDir": "./src",
|
||||
"esModuleInterop": true,
|
||||
"module": "ES2020",
|
||||
"target": "ES2015",
|
||||
"sourceMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": ["./*"]
|
||||
"include": ["./src"],
|
||||
"exclude": ["./src/*/stories"]
|
||||
}
|
||||
@@ -2,12 +2,13 @@
|
||||
"extends": "../../utilities/build/tsconfig-pakker.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./cjs",
|
||||
"rootDir": "./",
|
||||
"rootDir": "./src",
|
||||
"esModuleInterop": true,
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"sourceMap": false,
|
||||
"declaration": false
|
||||
},
|
||||
"include": ["./*"]
|
||||
"include": ["./src"],
|
||||
"exclude": ["./src/*/stories"]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tokens",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"private": false,
|
||||
"description": "Auto-generated design-tokens for NAV design-systems framework",
|
||||
"author": "NAV Designsystem team",
|
||||
|
||||
@@ -25,8 +25,12 @@ module.exports = {
|
||||
white: { value: white },
|
||||
darkgray: { value: darkgray },
|
||||
gray: {
|
||||
40: { value: "#b7b1a9" },
|
||||
60: { value: "#78706a" },
|
||||
90: { value: "#262626" },
|
||||
80: { value: "#4F4F4F" },
|
||||
60: { value: "#6A6A6A" },
|
||||
40: { value: "#A0A0A0" },
|
||||
20: { value: "#C9C9C9" },
|
||||
10: { value: "#F1F1F1" },
|
||||
},
|
||||
...Object.entries(baseColors).reduce(
|
||||
(colors, [name, color]) => ({
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Heading, Paragraph } from "@navikt/ds-react";
|
||||
import { Heading, Paragraph, Alert, Link } from "@navikt/ds-react";
|
||||
import Bash from "./code/Bash";
|
||||
import Import from "./code/Import";
|
||||
import Preview from "./code-preview/Preview";
|
||||
import Npm from "./npm/Npm";
|
||||
import TableOfContents from "./table-of-contents/TableOfContents";
|
||||
import { Knapp, Hovedknapp, Fareknapp, Flatknapp } from "nav-frontend-knapper";
|
||||
import { Settings } from "@navikt/ds-icons";
|
||||
|
||||
export default {
|
||||
h1: (props) => <Heading size="xxl" level={1} {...props} />,
|
||||
@@ -15,4 +17,11 @@ export default {
|
||||
Import,
|
||||
Npm,
|
||||
TableOfContents,
|
||||
Alert,
|
||||
Link,
|
||||
Knapp,
|
||||
Fareknapp,
|
||||
Flatknapp,
|
||||
Hovedknapp,
|
||||
Settings,
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useState } from "react";
|
||||
import Tabs from "../tabs/Tabs";
|
||||
import style from "./preview.module.css";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import Bash from "../code/Bash";
|
||||
import Prettier from "prettier/standalone";
|
||||
import ParserBabel from "prettier/parser-babel";
|
||||
import "./preview.css";
|
||||
|
||||
const prettierOptions = {
|
||||
semi: true,
|
||||
@@ -43,11 +43,11 @@ const Preview = ({
|
||||
.slice(0, -2)
|
||||
.replace(` data-reactroot=""`, "");
|
||||
|
||||
const tabs = !hideHtml || !!children ? ["REACT", "HTML/CSS"] : ["REACT"];
|
||||
const tabs = !hideHtml && !!children ? ["REACT", "HTML/CSS"] : ["REACT"];
|
||||
|
||||
return (
|
||||
<div className={style.wrapper}>
|
||||
{!!children && <div className={style.preview}>{children}</div>}
|
||||
<div className={"preview__wrapper"}>
|
||||
{!!children && <div className={"preview__container"}>{children}</div>}
|
||||
<Tabs tabs={tabs} onChange={(x) => handleChange(x)} />
|
||||
{tab === 0 && <Bash code={reactFormat} language="jsx" copy />}
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
.wrapper {
|
||||
.preview__wrapper {
|
||||
padding: 1rem;
|
||||
margin: 1rem 0;
|
||||
background-color: var(--bg-secondary);
|
||||
@@ -6,7 +6,7 @@
|
||||
outline: var(--outline);
|
||||
}
|
||||
|
||||
.preview {
|
||||
.preview__container {
|
||||
border-bottom: 2px solid var(--code-divider);
|
||||
padding-bottom: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
@@ -1,11 +1,11 @@
|
||||
import copy from "copy-to-clipboard";
|
||||
import Prism from "prismjs";
|
||||
import style from "./bash.module.css";
|
||||
import cl from "classnames";
|
||||
import { Files } from "@navikt/ds-icons";
|
||||
import { Popover } from "@navikt/ds-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import "prismjs/components/prism-jsx.min";
|
||||
import "./bash.css";
|
||||
|
||||
type PrismLanguages =
|
||||
| "extend"
|
||||
@@ -66,22 +66,22 @@ const Bash = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={style.preWrapper}>
|
||||
<pre className={style.pre}>
|
||||
<div className={"bash__preWrapper"}>
|
||||
<pre className={"bash__pre"}>
|
||||
<code
|
||||
className={cl(style.code, {
|
||||
[style.codeCopy]: copy,
|
||||
[style.terminal]: terminal,
|
||||
className={cl(code, {
|
||||
bash__codeCopy: copy,
|
||||
bash__terminal: terminal,
|
||||
})}
|
||||
{...props}
|
||||
dangerouslySetInnerHTML={{ __html: highlighted }}
|
||||
/>
|
||||
{copy && (
|
||||
<>
|
||||
<div className={style.buttonBackground}>
|
||||
<div className={"bash__button"}>
|
||||
<button
|
||||
ref={buttonRef}
|
||||
className={style.copyButton}
|
||||
className={"bash__copyButton"}
|
||||
onClick={() => handleCopy()}
|
||||
>
|
||||
<Files />
|
||||
@@ -91,7 +91,6 @@ const Bash = ({
|
||||
anchorEl={buttonRef.current}
|
||||
open={openPopover}
|
||||
onClose={() => setOpenPopover(false)}
|
||||
size="small"
|
||||
placement="auto-start"
|
||||
/* arrow={false} */
|
||||
>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.pre {
|
||||
.bash__pre {
|
||||
padding: 1rem;
|
||||
margin: 1rem 0 0 0;
|
||||
overflow: auto;
|
||||
@@ -11,26 +11,26 @@
|
||||
box-shadow: inset -4px 0 0 var(--bg-code);
|
||||
}
|
||||
|
||||
.preWrapper {
|
||||
.bash__preWrapper {
|
||||
position: relative;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.terminal {
|
||||
.bash__terminal {
|
||||
color: var(--color-code);
|
||||
}
|
||||
|
||||
.code {
|
||||
.bash__code {
|
||||
padding-right: 4rem;
|
||||
color: var(--color-code);
|
||||
}
|
||||
|
||||
.terminal::before {
|
||||
.bash__terminal::before {
|
||||
content: "$ ";
|
||||
}
|
||||
|
||||
.buttonBackground {
|
||||
.bash__button {
|
||||
width: 3rem;
|
||||
background-color: var(--bg-code);
|
||||
position: absolute;
|
||||
@@ -42,7 +42,7 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.copyButton {
|
||||
.bash__copyButton {
|
||||
background-color: var(--bg-code);
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
@@ -54,11 +54,11 @@
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
.copyButton:hover {
|
||||
.bash__copyButton:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.copyButton:focus {
|
||||
.bash__copyButton:focus {
|
||||
outline: var(--outline-code);
|
||||
outline-offset: var(--outline-offset);
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import cl from "classnames";
|
||||
import Header from "./header/Header";
|
||||
import Sidebar from "./sidebar/Sidebar";
|
||||
import { Grid, Cell, ContentContainer } from "@navikt/ds-react";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
import useKeypress from "react-use-keypress";
|
||||
import { useEffect, useState } from "react";
|
||||
import style from "./layout.module.css";
|
||||
import "./theme.css";
|
||||
import "./layout.css";
|
||||
|
||||
interface LayoutProps {
|
||||
children?: React.ReactNode;
|
||||
@@ -27,7 +29,7 @@ const Layout = ({ children, menu }: LayoutProps) => {
|
||||
}, [small]);
|
||||
|
||||
return (
|
||||
<div className={style.pageWrapper + " lightTheme"}>
|
||||
<div className={cl("pageWrapper", "lightTheme")}>
|
||||
<Header sidebar={sidebar} onSidebarChange={(x) => setSidebar(x)} />
|
||||
<Sidebar
|
||||
menu={menu}
|
||||
@@ -36,10 +38,10 @@ const Layout = ({ children, menu }: LayoutProps) => {
|
||||
onSidebarChange={(x) => setSidebar(x)}
|
||||
/>
|
||||
{/* TEMP UNTIL LAYOUT UPDATE */}
|
||||
<main className={style.contentWrapper}>
|
||||
<main className={"contentWrapper"}>
|
||||
<ContentContainer>
|
||||
<Grid>
|
||||
<Cell className={style.content} xs={12} sm={12} md={12} lg={12}>
|
||||
<Cell className={"content"} xs={12} sm={12} md={12} lg={12}>
|
||||
{children}
|
||||
</Cell>
|
||||
</Grid>
|
||||
|
||||
@@ -2,7 +2,7 @@ import Link from "next/link";
|
||||
import NavLogo from "../../assets/navLogo.svg";
|
||||
import { HamburgerFilled } from "@navikt/ds-icons";
|
||||
import cl from "classnames";
|
||||
import style from "../layout.module.css";
|
||||
import "../layout.css";
|
||||
|
||||
interface HeaderProps {
|
||||
className?: string;
|
||||
@@ -17,20 +17,20 @@ const Header = ({
|
||||
...props
|
||||
}: HeaderProps) => {
|
||||
return (
|
||||
<header className={style.header}>
|
||||
<header className={"header"}>
|
||||
<button
|
||||
onClick={() => onSidebarChange(!sidebar)}
|
||||
className={cl(style.header__link, style.header__icon, {
|
||||
[style["header__icon--hidden"]]: sidebar,
|
||||
className={cl("header__link", "header__icon", {
|
||||
"header__icon--hidden": sidebar,
|
||||
})}
|
||||
tabIndex={!sidebar ? 0 : -1}
|
||||
>
|
||||
<HamburgerFilled />
|
||||
</button>
|
||||
<Link href="/">
|
||||
<button className={style.header__link}>
|
||||
<button className={"header__link"}>
|
||||
<NavLogo />
|
||||
<span className={style["header__link-title"]}>NAV Designsystem</span>
|
||||
<span className={"header__link-title"}>NAV Designsystem</span>
|
||||
</button>
|
||||
</Link>
|
||||
</header>
|
||||
|
||||
File renamed without changes.
@@ -1,47 +1,34 @@
|
||||
import { NavdsPage } from "../../../src/pages";
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
AccordionMenu,
|
||||
AccordionMenuCollapsable,
|
||||
AccordionMenuItem,
|
||||
} from "@navikt/ds-react";
|
||||
|
||||
const MenuLink = (node) => {
|
||||
const { asPath } = useRouter();
|
||||
|
||||
/* TODO: Replace with accordion-menu when ready */
|
||||
const subLinks = (page: NavdsPage) => {
|
||||
return (
|
||||
<div key={page.title}>
|
||||
<span>{page.title}</span>
|
||||
<ul id={"collapse " + page.title}>{parsePages(page.children)}</ul>
|
||||
</div>
|
||||
<Link href={node.pathName} passHref>
|
||||
<AccordionMenuItem active={asPath === node.pathName}>
|
||||
{node.title}
|
||||
</AccordionMenuItem>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const parsePages = (pages) => {
|
||||
return pages?.map((page) => {
|
||||
if (page.pathName) {
|
||||
return (
|
||||
<li key={page.pathName}>
|
||||
<Link href={page.pathName}>
|
||||
{page.title ? page.title : parseTitle(page.pathName)}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
} else if (page.title && page.children) {
|
||||
return subLinks(page);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const parseTitle = (path: string) =>
|
||||
path
|
||||
.split("/")
|
||||
.pop()
|
||||
.replace(/^\w/, (c) => c.toUpperCase())
|
||||
.replace("-", " ") || "ERR: Default Title";
|
||||
|
||||
const Menu = ({ menu }) => {
|
||||
return (
|
||||
<nav>
|
||||
<ul>{parsePages(menu)}</ul>
|
||||
</nav>
|
||||
const mapToComponents = (node) =>
|
||||
node.children ? (
|
||||
<AccordionMenuCollapsable key={node.title} title={node.title}>
|
||||
{node.children.map(mapToComponents)}
|
||||
</AccordionMenuCollapsable>
|
||||
) : (
|
||||
<MenuLink key={node.title} {...node} />
|
||||
);
|
||||
};
|
||||
|
||||
const Menu = ({ menu }) => (
|
||||
<AccordionMenu>{menu.map(mapToComponents)}</AccordionMenu>
|
||||
);
|
||||
|
||||
export default Menu;
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "classnames";
|
||||
import { Close } from "@navikt/ds-icons";
|
||||
import style from "../layout.module.css";
|
||||
import "../layout.css";
|
||||
import Menu from "./Menu";
|
||||
|
||||
interface SidebarProps {
|
||||
@@ -11,27 +11,32 @@ interface SidebarProps {
|
||||
menu: any[];
|
||||
}
|
||||
|
||||
const Sidebar = ({ sidebar, small, onSidebarChange, menu }: SidebarProps) => {
|
||||
const Sidebar = ({
|
||||
sidebar,
|
||||
small,
|
||||
onSidebarChange,
|
||||
menu = [],
|
||||
}: SidebarProps) => {
|
||||
return (
|
||||
<>
|
||||
{sidebar && small && (
|
||||
<div
|
||||
onClick={() => onSidebarChange(false)}
|
||||
className={cl(style["sidebar--overlay"], {
|
||||
[style["sidebar--overlay--fade"]]: sidebar,
|
||||
className={cl("sidebar--overlay", {
|
||||
"sidebar--overlay--fade": sidebar,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={cl(style.sidebar, {
|
||||
[style.sidebar__mobile]: small,
|
||||
[style["sidebar__mobile--open"]]: small && sidebar,
|
||||
className={cl("sidebar", {
|
||||
sidebar__mobile: small,
|
||||
"sidebar__mobile--open": small && sidebar,
|
||||
})}
|
||||
>
|
||||
{small && sidebar && (
|
||||
<button
|
||||
onClick={() => onSidebarChange(!sidebar)}
|
||||
className={style.sidebar__icon}
|
||||
className={"sidebar__icon"}
|
||||
>
|
||||
<Close />
|
||||
</button>
|
||||
|
||||
File renamed without changes.
@@ -1,6 +1,6 @@
|
||||
import getConfig from "next/config";
|
||||
import Import from "../code/Import";
|
||||
import style from "./npm.module.css";
|
||||
import "./npm.css";
|
||||
|
||||
const Npm = ({
|
||||
packName,
|
||||
@@ -21,7 +21,7 @@ const Npm = ({
|
||||
<div>
|
||||
{packs.map(({ name, data }) => (
|
||||
<div key={name}>
|
||||
<span className={style.badges}>
|
||||
<span className={"npm__.badges"}>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.badges {
|
||||
.npm__badges {
|
||||
display: flex;
|
||||
column-gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useContext } from "react";
|
||||
import { Next } from "@navikt/ds-icons";
|
||||
import { Heading } from "@navikt/ds-react";
|
||||
import style from "./tableOfContents.module.css";
|
||||
import { PagePropsContext } from "../../pages/_app";
|
||||
import "./tableOfContents.css";
|
||||
|
||||
const TableOfContents = () => {
|
||||
const { tableOfContents } = useContext<any>(PagePropsContext);
|
||||
@@ -12,9 +12,9 @@ const TableOfContents = () => {
|
||||
<Heading size="large" level={2}>
|
||||
Innhold
|
||||
</Heading>
|
||||
<ul className={style.ul}>
|
||||
<ul className={"toc__ul"}>
|
||||
{tableOfContents?.map(({ heading, slug }) => (
|
||||
<li key={slug} className={style.li}>
|
||||
<li key={slug} className={"toc__li"}>
|
||||
<Next
|
||||
aria-label="Pil peker på lenke"
|
||||
focusable="false"
|
||||
|
||||
+8
-8
@@ -1,10 +1,10 @@
|
||||
.ul {
|
||||
.toc__ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.li {
|
||||
.toc__li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
font-size: var(--navds-font-size-medium);
|
||||
@@ -18,26 +18,26 @@
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.li > svg {
|
||||
.toc__li > svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.li:hover {
|
||||
.toc__li:hover {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.li > a {
|
||||
.toc__li > a {
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.li:hover > a,
|
||||
.li:hover > svg {
|
||||
.toc__li:hover > a,
|
||||
.toc__li:hover > svg {
|
||||
color: var(--navds-color-darkgray);
|
||||
transition: color 150ms linear;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.li > svg + a:focus {
|
||||
.toc__li > svg + a:focus {
|
||||
color: white;
|
||||
transition: color 0ms linear;
|
||||
background-color: var(--navds-color-blue-80);
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import style from "./tabs.module.css";
|
||||
import "./tabs.css";
|
||||
import cl from "classnames";
|
||||
|
||||
interface TabsProps {
|
||||
@@ -16,11 +16,11 @@ const Tabs = ({ onChange, tabs, ...props }: TabsProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<ul className={style.ul}>
|
||||
<ul className={"tabs__ul"}>
|
||||
{tabs.map((t, x) => (
|
||||
<li key={t + x} className={style.li}>
|
||||
<li key={t + x} className={"tabs__li"}>
|
||||
<button
|
||||
className={cl(style.button, { [style.buttonActive]: x === tab })}
|
||||
className={cl("tabs__button", { tabs__buttonActive: x === tab })}
|
||||
onClick={() => handleClick(x)}
|
||||
>
|
||||
{t}
|
||||
|
||||
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user