@ndla/ui 12.0.1 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/Breadcrumb/Breadcrumb.js +101 -24
- package/es/Breadcrumb/BreadcrumbItem.js +70 -18
- package/es/Breadcrumb/HeaderBreadcrumb.js +63 -0
- package/es/Breadcrumb/HomeBreadcrumb.js +117 -0
- package/es/Breadcrumb/index.js +2 -1
- package/es/Breadcrumblist/index.js +1 -2
- package/es/all.css +1 -1
- package/es/index.js +1 -1
- package/es/locale/messages-en.js +59 -18
- package/es/locale/messages-nb.js +58 -17
- package/es/locale/messages-nn.js +59 -18
- package/es/locale/messages-se.js +56 -15
- package/es/locale/messages-sma.js +56 -15
- package/lib/Breadcrumb/Breadcrumb.d.ts +10 -12
- package/lib/Breadcrumb/Breadcrumb.js +104 -32
- package/lib/Breadcrumb/BreadcrumbItem.d.ts +15 -7
- package/lib/Breadcrumb/BreadcrumbItem.js +68 -20
- package/lib/Breadcrumb/HeaderBreadcrumb.d.ts +14 -0
- package/lib/Breadcrumb/HeaderBreadcrumb.js +76 -0
- package/lib/Breadcrumb/HomeBreadcrumb.d.ts +15 -0
- package/lib/Breadcrumb/HomeBreadcrumb.js +127 -0
- package/lib/Breadcrumb/index.d.ts +3 -1
- package/lib/Breadcrumb/index.js +11 -3
- package/lib/Breadcrumblist/index.js +0 -1
- package/lib/all.css +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +10 -3
- package/lib/locale/messages-en.d.ts +43 -2
- package/lib/locale/messages-en.js +59 -18
- package/lib/locale/messages-nb.d.ts +43 -2
- package/lib/locale/messages-nb.js +58 -17
- package/lib/locale/messages-nn.d.ts +43 -2
- package/lib/locale/messages-nn.js +59 -18
- package/lib/locale/messages-se.d.ts +43 -2
- package/lib/locale/messages-se.js +56 -15
- package/lib/locale/messages-sma.d.ts +44 -3
- package/lib/locale/messages-sma.js +56 -15
- package/package.json +10 -10
- package/src/Breadcrumb/Breadcrumb.tsx +76 -40
- package/src/Breadcrumb/BreadcrumbItem.tsx +82 -18
- package/src/Breadcrumb/HeaderBreadcrumb.tsx +67 -0
- package/src/Breadcrumb/HomeBreadcrumb.tsx +88 -0
- package/src/Breadcrumb/index.ts +5 -1
- package/src/Breadcrumblist/index.tsx +0 -1
- package/src/index.ts +2 -1
- package/src/locale/__tests__/translations-test.ts +10 -0
- package/src/locale/messages-en.ts +55 -14
- package/src/locale/messages-nb.ts +54 -13
- package/src/locale/messages-nn.ts +56 -15
- package/src/locale/messages-se.ts +52 -11
- package/src/locale/messages-sma.ts +53 -12
- package/src/main.scss +0 -2
- package/es/Breadcrumb/BreadcrumbBlock.js +0 -90
- package/lib/Breadcrumb/BreadcrumbBlock.d.ts +0 -15
- package/lib/Breadcrumb/BreadcrumbBlock.js +0 -105
- package/src/.DS_Store +0 -0
- package/src/Breadcrumb/BreadcrumbBlock.tsx +0 -80
- package/src/Breadcrumb/component.breadcrumb-block.scss +0 -98
- package/src/Breadcrumb/component.breadcrumb.scss +0 -104
|
@@ -411,7 +411,7 @@ var messages = _objectSpread(_objectSpread({
|
|
|
411
411
|
rules: 'Regler for bruk av bildet:'
|
|
412
412
|
},
|
|
413
413
|
images: {
|
|
414
|
-
heading: 'Slik
|
|
414
|
+
heading: 'Slik gjenbruker du bilder',
|
|
415
415
|
description: 'Husk å kopiere teksten som skal legges ved bildet der du bruker det.',
|
|
416
416
|
rules: 'Regler for bruk av bildet:',
|
|
417
417
|
itemImage: {
|
|
@@ -425,18 +425,18 @@ var messages = _objectSpread(_objectSpread({
|
|
|
425
425
|
title: 'Tittel'
|
|
426
426
|
},
|
|
427
427
|
text: {
|
|
428
|
-
heading: 'Slik
|
|
428
|
+
heading: 'Slik gjenbruker du teksten',
|
|
429
429
|
description: 'Husk å henvise til kilden når du gjenbruker tekst.',
|
|
430
430
|
rules: 'Regler for bruk av teksten:',
|
|
431
431
|
published: 'Publiseringsdato'
|
|
432
432
|
},
|
|
433
433
|
audio: {
|
|
434
|
-
heading: 'Slik
|
|
434
|
+
heading: 'Slik gjenbruker du lydfiler',
|
|
435
435
|
description: 'Husk å kopiere teksten som skal legges ved lydfilen der du bruker den.',
|
|
436
436
|
rules: 'Regler for bruk av lydfilen:'
|
|
437
437
|
},
|
|
438
438
|
video: {
|
|
439
|
-
heading: 'Slik
|
|
439
|
+
heading: 'Slik gjenbruker du videoer',
|
|
440
440
|
description: 'Husk å kopiere teksten som skal legges ved videoen der du bruker den.',
|
|
441
441
|
rules: 'Regler for bruk av videoen:',
|
|
442
442
|
itemImage: {
|
|
@@ -444,31 +444,42 @@ var messages = _objectSpread(_objectSpread({
|
|
|
444
444
|
}
|
|
445
445
|
},
|
|
446
446
|
other: {
|
|
447
|
-
heading: 'Slik
|
|
447
|
+
heading: 'Slik gjenbruker du annet innhold',
|
|
448
448
|
description: 'Du finner retningslinjene for bruk av innholdet i innholdselementet',
|
|
449
449
|
itemImage: {
|
|
450
450
|
ariaLabel: 'Åpne i nytt vindu'
|
|
451
451
|
}
|
|
452
452
|
},
|
|
453
453
|
h5p: {
|
|
454
|
-
heading: 'Slik
|
|
454
|
+
heading: 'Slik gjenbruker du H5P-innhold',
|
|
455
455
|
description: 'Du finner retningslinjene for bruk av innholdet i H5P-elementet',
|
|
456
456
|
rules: 'Regler for bruk av H5P:'
|
|
457
457
|
},
|
|
458
458
|
concept: {
|
|
459
|
-
|
|
459
|
+
embedlink: {
|
|
460
|
+
heading: 'Slik viser du forklaringa i anna innhald',
|
|
461
|
+
description: 'Denne lenka viser forklaringa utan kontekst (meny og botntekst)',
|
|
462
|
+
copyTitle: 'Kopier innbyggingslenke',
|
|
463
|
+
hasCopiedTitle: 'Innbyggingslenke kopiert'
|
|
464
|
+
},
|
|
465
|
+
heading: 'Slik gjenbruker du forklaringer',
|
|
460
466
|
description: 'Du finner retningslinjene for bruk av innholdet i forklaring-elementet',
|
|
461
467
|
rules: 'Regler for bruk av forklaring:',
|
|
462
468
|
title: 'Tittel'
|
|
463
469
|
},
|
|
464
470
|
files: {
|
|
465
|
-
heading: 'Slik
|
|
471
|
+
heading: 'Slik gjenbruker du filer',
|
|
466
472
|
description: 'Husk å kopier teksten som skal legges ved filen der du bruker den.',
|
|
467
473
|
rules: 'Regler for bruk av filen:',
|
|
468
474
|
itemImage: {
|
|
469
475
|
ariaLabel: 'Åpne i nytt vindu'
|
|
470
476
|
}
|
|
471
|
-
}
|
|
477
|
+
},
|
|
478
|
+
title: 'Tittel',
|
|
479
|
+
originator: 'Opphavar',
|
|
480
|
+
rightsholder: 'Rettshavar',
|
|
481
|
+
source: 'Kjelde',
|
|
482
|
+
published: 'Publiseringsdato'
|
|
472
483
|
},
|
|
473
484
|
errorMessage: {
|
|
474
485
|
title: 'Ops, noe gikk galt',
|
|
@@ -499,8 +510,10 @@ var messages = _objectSpread(_objectSpread({
|
|
|
499
510
|
newsletterAria: 'Meld deg på vårt nyhetsbrev',
|
|
500
511
|
youtube: 'NDLA på YouTube',
|
|
501
512
|
youtubeAria: 'NDLA på YouTube',
|
|
502
|
-
|
|
503
|
-
|
|
513
|
+
linkedin: 'NDLA på LinkedIn',
|
|
514
|
+
linkedinAria: 'Besøk NDLA på LinkedIn',
|
|
515
|
+
instagram: 'NDLA på Instagram',
|
|
516
|
+
instagramAria: 'Besøk NDLA på Instagram',
|
|
504
517
|
sharePage: 'Del denne siden',
|
|
505
518
|
sharePageAria: 'Del denne siden'
|
|
506
519
|
},
|
|
@@ -563,6 +576,10 @@ var messages = _objectSpread(_objectSpread({
|
|
|
563
576
|
search: {
|
|
564
577
|
placeholder: 'Søk'
|
|
565
578
|
},
|
|
579
|
+
embedlink: {
|
|
580
|
+
copyTitle: 'Kopier innbyggingskode',
|
|
581
|
+
hasCopiedTitle: 'Innbyggingskode kopiert'
|
|
582
|
+
},
|
|
566
583
|
filters: {
|
|
567
584
|
subject: {
|
|
568
585
|
useFilter: 'Bruk filter',
|
|
@@ -578,7 +595,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
578
595
|
useFilter: 'Bruk filter',
|
|
579
596
|
openFilter: 'Filtrer',
|
|
580
597
|
closeFilter: 'Lukk filter',
|
|
581
|
-
heading: 'Filter'
|
|
598
|
+
heading: 'Filter',
|
|
599
|
+
filteredBy: 'Filtrert på'
|
|
582
600
|
},
|
|
583
601
|
alphabet: {
|
|
584
602
|
letterFilter: 'Vis innhald på (bokstaven) {{letter}}.'
|
|
@@ -728,7 +746,15 @@ var messages = _objectSpread(_objectSpread({
|
|
|
728
746
|
},
|
|
729
747
|
createdBy: {
|
|
730
748
|
content: 'Ressursen',
|
|
731
|
-
text: 'er hentet fra'
|
|
749
|
+
text: 'er hentet fra',
|
|
750
|
+
concept: {
|
|
751
|
+
content: 'Forklaringa',
|
|
752
|
+
text: 'er utarbeida av'
|
|
753
|
+
},
|
|
754
|
+
listing: {
|
|
755
|
+
content: 'Lista',
|
|
756
|
+
text: 'er utarbeida av'
|
|
757
|
+
}
|
|
732
758
|
},
|
|
733
759
|
fagfornyelse: {
|
|
734
760
|
frontpage: {
|
|
@@ -761,7 +787,7 @@ var messages = _objectSpread(_objectSpread({
|
|
|
761
787
|
frontpageMenu: {
|
|
762
788
|
program: 'Utdanningsprogram',
|
|
763
789
|
allsubjects: 'Alle fag',
|
|
764
|
-
cursorText: 'Se smakebiter fra fag
|
|
790
|
+
cursorText: 'Se smakebiter fra fag under utvikling.'
|
|
765
791
|
},
|
|
766
792
|
navigation: {
|
|
767
793
|
showLongerDescription: 'Vis hele emnebeskrivelsen',
|
|
@@ -782,7 +808,7 @@ var messages = _objectSpread(_objectSpread({
|
|
|
782
808
|
title: 'Tittel',
|
|
783
809
|
image: {
|
|
784
810
|
altText: 'Alt-tekst',
|
|
785
|
-
|
|
811
|
+
caption: 'Bilettekst',
|
|
786
812
|
type: 'Filtype',
|
|
787
813
|
width: 'Breidde',
|
|
788
814
|
height: 'Høgde',
|
|
@@ -901,6 +927,21 @@ var messages = _objectSpread(_objectSpread({
|
|
|
901
927
|
myAccount: 'Min konto',
|
|
902
928
|
favourites: 'Favoritter',
|
|
903
929
|
help: 'Hjelp'
|
|
930
|
+
},
|
|
931
|
+
labels: {
|
|
932
|
+
category: 'Kategori',
|
|
933
|
+
subject: 'Fag',
|
|
934
|
+
other: 'Anna'
|
|
935
|
+
},
|
|
936
|
+
listingPage: {
|
|
937
|
+
or: 'eller',
|
|
938
|
+
noFilters: 'Har ikkje noko å filtrera',
|
|
939
|
+
loadMore: 'Last meir'
|
|
940
|
+
},
|
|
941
|
+
siteNav: {
|
|
942
|
+
search: 'Søk',
|
|
943
|
+
contact: 'Kontakt',
|
|
944
|
+
help: 'Hjelp'
|
|
904
945
|
}
|
|
905
946
|
});
|
|
906
947
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"description": "UI component library for NDLA.",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -31,17 +31,17 @@
|
|
|
31
31
|
"types"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ndla/button": "^2.2.
|
|
35
|
-
"@ndla/carousel": "^1.2.
|
|
34
|
+
"@ndla/button": "^2.2.1",
|
|
35
|
+
"@ndla/carousel": "^1.2.7",
|
|
36
36
|
"@ndla/core": "^2.1.1",
|
|
37
37
|
"@ndla/hooks": "^1.1.4",
|
|
38
|
-
"@ndla/icons": "^1.7.
|
|
39
|
-
"@ndla/licenses": "^4.1.
|
|
40
|
-
"@ndla/modal": "^1.2.
|
|
41
|
-
"@ndla/notion": "^3.1.
|
|
42
|
-
"@ndla/safelink": "^2.0.
|
|
38
|
+
"@ndla/icons": "^1.7.1",
|
|
39
|
+
"@ndla/licenses": "^4.1.2",
|
|
40
|
+
"@ndla/modal": "^1.2.8",
|
|
41
|
+
"@ndla/notion": "^3.1.8",
|
|
42
|
+
"@ndla/safelink": "^2.0.2",
|
|
43
43
|
"@ndla/switch": "^0.1.5",
|
|
44
|
-
"@ndla/tabs": "^1.1.
|
|
44
|
+
"@ndla/tabs": "^1.1.6",
|
|
45
45
|
"@ndla/tooltip": "^0.3.5",
|
|
46
46
|
"@ndla/util": "^3.0.0",
|
|
47
47
|
"@reach/menu-button": "^0.16.2",
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"publishConfig": {
|
|
82
82
|
"access": "public"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "e8c0504caecab0d3f093d57ccb007f529d838cd4"
|
|
85
85
|
}
|
|
@@ -6,47 +6,83 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React, { ReactNode } from 'react';
|
|
10
|
-
import
|
|
9
|
+
import React, { ReactNode, useRef } from 'react';
|
|
10
|
+
import { useComponentSize, useIsomorphicLayoutEffect } from '@ndla/hooks';
|
|
11
11
|
import { uuid } from '@ndla/util';
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import BreadcrumbItem from './BreadcrumbItem';
|
|
15
|
-
|
|
16
|
-
const classes: BEMHelper<ReturnObject> = BEMHelper({
|
|
17
|
-
name: 'breadcrumb',
|
|
18
|
-
prefix: 'c-',
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export interface BreadcrumbItemI {
|
|
22
|
-
to: string;
|
|
23
|
-
name: string;
|
|
24
|
-
}
|
|
12
|
+
import styled from '@emotion/styled';
|
|
13
|
+
import { useTranslation } from 'react-i18next';
|
|
14
|
+
import BreadcrumbItem, { IndexedBreadcrumbItem, SimpleBreadcrumbItem } from './BreadcrumbItem';
|
|
25
15
|
|
|
26
|
-
interface Props
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
interface Props {
|
|
17
|
+
items: SimpleBreadcrumbItem[];
|
|
18
|
+
autoCollapse?: boolean;
|
|
19
|
+
collapseFirst?: boolean;
|
|
20
|
+
renderItem?: (item: IndexedBreadcrumbItem, totalCount: number) => ReactNode;
|
|
21
|
+
renderSeparator?: (item: IndexedBreadcrumbItem, totalCount: number) => ReactNode;
|
|
30
22
|
}
|
|
31
23
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
24
|
+
const BreadcrumbNav = styled.nav``;
|
|
25
|
+
|
|
26
|
+
const StyledList = styled.ol`
|
|
27
|
+
display: inline-block;
|
|
28
|
+
padding-left: 0;
|
|
29
|
+
margin-bottom: 0;
|
|
30
|
+
margin-top: 0;
|
|
31
|
+
list-style: none;
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const Breadcrumb = ({ items, autoCollapse, renderItem, renderSeparator, collapseFirst }: Props) => {
|
|
35
|
+
const { t } = useTranslation();
|
|
36
|
+
const olRef = useRef<any>();
|
|
37
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
38
|
+
// No idiomatic way of dealing with sets of refs yet
|
|
39
|
+
// See: https://github.com/facebook/react/issues/14072#issuecomment-446777406
|
|
40
|
+
const breadcrumbItemRefs = useRef(new Map()).current;
|
|
41
|
+
const size = useComponentSize(containerRef);
|
|
42
|
+
|
|
43
|
+
useIsomorphicLayoutEffect(() => {
|
|
44
|
+
if (!autoCollapse) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Create an array of all breadcrumb item refs
|
|
48
|
+
const items = Array.from(breadcrumbItemRefs).map(([key, value]) => value);
|
|
49
|
+
|
|
50
|
+
// Clear max width on all items
|
|
51
|
+
items.forEach((el) => {
|
|
52
|
+
el.setMaxWidth('none');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Set maxWidth on breadcrumb text items iteratively until
|
|
56
|
+
// the ordered list fits on a single line. It's on a single line
|
|
57
|
+
// if the height of the list is less then 70.
|
|
58
|
+
items.forEach((el) => {
|
|
59
|
+
if (olRef.current.offsetHeight > 60) {
|
|
60
|
+
el.setMaxWidth('40px');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}, [size]);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<BreadcrumbNav ref={containerRef} aria-label={t('breadcrumb.breadcrumb')}>
|
|
67
|
+
<StyledList ref={olRef}>
|
|
68
|
+
{items.map((item, index) => (
|
|
69
|
+
<BreadcrumbItem
|
|
70
|
+
autoCollapse={autoCollapse}
|
|
71
|
+
renderItem={renderItem}
|
|
72
|
+
renderSeparator={renderSeparator}
|
|
73
|
+
ref={(element) =>
|
|
74
|
+
element === null || (!collapseFirst && index === 0) // skip first item which is never truncated
|
|
75
|
+
? breadcrumbItemRefs.delete(item.to)
|
|
76
|
+
: breadcrumbItemRefs.set(item.to, element)
|
|
77
|
+
}
|
|
78
|
+
key={uuid()}
|
|
79
|
+
totalCount={items.length}
|
|
80
|
+
item={{ ...item, index }}
|
|
81
|
+
/>
|
|
82
|
+
))}
|
|
83
|
+
</StyledList>
|
|
84
|
+
</BreadcrumbNav>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default Breadcrumb;
|
|
@@ -9,37 +9,101 @@
|
|
|
9
9
|
import React, { useRef, useImperativeHandle, ReactNode, forwardRef } from 'react';
|
|
10
10
|
import { ChevronRight } from '@ndla/icons/common';
|
|
11
11
|
import SafeLink from '@ndla/safelink';
|
|
12
|
-
import
|
|
12
|
+
import styled from '@emotion/styled';
|
|
13
|
+
import { mq, spacing, breakpoints } from '@ndla/core';
|
|
14
|
+
import { css } from '@emotion/core';
|
|
13
15
|
|
|
14
|
-
interface
|
|
15
|
-
classes: BEMHelper<ReturnObject>;
|
|
16
|
-
isCurrent: boolean;
|
|
17
|
-
children: ReactNode;
|
|
16
|
+
export interface SimpleBreadcrumbItem {
|
|
18
17
|
to: string | Partial<Location>;
|
|
19
|
-
home: boolean;
|
|
20
18
|
name: string;
|
|
21
|
-
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface IndexedBreadcrumbItem extends SimpleBreadcrumbItem {
|
|
22
|
+
index: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface BreadcrumbRenderProps {
|
|
26
|
+
item: IndexedBreadcrumbItem;
|
|
27
|
+
totalCount: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface AutoCollapseProps {
|
|
31
|
+
autoCollapse?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const StyledListItem = styled.li<AutoCollapseProps>`
|
|
35
|
+
margin-bottom: 0;
|
|
36
|
+
margin-left: 0;
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
:before {
|
|
40
|
+
display: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
${({ autoCollapse }) =>
|
|
44
|
+
!autoCollapse &&
|
|
45
|
+
css`
|
|
46
|
+
${mq.range({ until: breakpoints.tablet })} {
|
|
47
|
+
display: block;
|
|
48
|
+
}
|
|
49
|
+
`}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const CollapseContainer = styled.div<AutoCollapseProps>`
|
|
53
|
+
display: inline-block;
|
|
54
|
+
${({ autoCollapse }) =>
|
|
55
|
+
autoCollapse &&
|
|
56
|
+
css`
|
|
57
|
+
text-overflow: ellipsis;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
display: inline-block;
|
|
61
|
+
`}
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const StyledChevron = styled(ChevronRight)`
|
|
65
|
+
margin: ${spacing.xxsmall};
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const StyledSafeLink = styled(SafeLink)`
|
|
69
|
+
color: inherit;
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
interface Props {
|
|
73
|
+
item: IndexedBreadcrumbItem;
|
|
74
|
+
autoCollapse?: boolean;
|
|
75
|
+
totalCount: number;
|
|
76
|
+
renderItem?: (item: IndexedBreadcrumbItem, totalCount: number) => ReactNode;
|
|
77
|
+
renderSeparator?: (item: IndexedBreadcrumbItem, totalCount: number) => ReactNode;
|
|
22
78
|
}
|
|
23
79
|
|
|
24
80
|
const BreadcrumbItem = forwardRef<any, Props>(
|
|
25
|
-
({
|
|
81
|
+
({ renderItem, renderSeparator, item, totalCount, autoCollapse }, ref) => {
|
|
26
82
|
const liRef = useRef<any>();
|
|
83
|
+
|
|
27
84
|
useImperativeHandle(ref, () => ({
|
|
28
85
|
setMaxWidth: (maxWidth: number) => {
|
|
29
86
|
liRef.current.children[0].style.maxWidth = maxWidth;
|
|
30
87
|
},
|
|
31
88
|
}));
|
|
89
|
+
|
|
90
|
+
const { to, name, index } = item;
|
|
91
|
+
const isLast = index === totalCount - 1;
|
|
32
92
|
return (
|
|
33
|
-
<
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
93
|
+
<StyledListItem ref={liRef} autoCollapse={autoCollapse}>
|
|
94
|
+
<CollapseContainer autoCollapse={autoCollapse}>
|
|
95
|
+
{renderItem ? (
|
|
96
|
+
renderItem(item, totalCount)
|
|
97
|
+
) : isLast ? (
|
|
98
|
+
<span>{name}</span>
|
|
99
|
+
) : (
|
|
100
|
+
<StyledSafeLink to={to}>
|
|
101
|
+
<span>{name}</span>
|
|
102
|
+
</StyledSafeLink>
|
|
103
|
+
)}
|
|
104
|
+
</CollapseContainer>
|
|
105
|
+
{renderSeparator ? renderSeparator(item, totalCount) : !isLast && <StyledChevron />}
|
|
106
|
+
</StyledListItem>
|
|
43
107
|
);
|
|
44
108
|
},
|
|
45
109
|
);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import styled from '@emotion/styled';
|
|
10
|
+
import { colors, fonts, spacing } from '@ndla/core';
|
|
11
|
+
import { ChevronRight } from '@ndla/icons/common';
|
|
12
|
+
import SafeLink from '@ndla/safelink';
|
|
13
|
+
import React from 'react';
|
|
14
|
+
import Breadcrumb from './Breadcrumb';
|
|
15
|
+
import { IndexedBreadcrumbItem, SimpleBreadcrumbItem } from './BreadcrumbItem';
|
|
16
|
+
|
|
17
|
+
const StyledHeaderSafeLink = styled(SafeLink)`
|
|
18
|
+
${fonts.sizes(14)};
|
|
19
|
+
font-weight: ${fonts.weight.bold};
|
|
20
|
+
color: ${colors.brand.primary};
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const StyledBlueRightChevron = styled(ChevronRight)`
|
|
24
|
+
color: ${colors.brand.primary};
|
|
25
|
+
margin: ${spacing.xxsmall};
|
|
26
|
+
`;
|
|
27
|
+
const StyledBlueSpan = styled.span`
|
|
28
|
+
color: ${colors.brand.primary};
|
|
29
|
+
`;
|
|
30
|
+
const StyledBlueSafeLink = styled(SafeLink)`
|
|
31
|
+
color: ${colors.brand.primary};
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
interface Props {
|
|
35
|
+
items: SimpleBreadcrumbItem[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const HeaderBreadcrumb = ({ items }: Props) => {
|
|
39
|
+
const renderItem = (item: IndexedBreadcrumbItem, totalCount: number) => {
|
|
40
|
+
if (item.index === totalCount - 1) {
|
|
41
|
+
return <StyledBlueSpan>{item.name}</StyledBlueSpan>;
|
|
42
|
+
}
|
|
43
|
+
return <StyledBlueSafeLink to={item.to}>{item.name}</StyledBlueSafeLink>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const renderSeparator = (item: IndexedBreadcrumbItem, totalCount: number) => {
|
|
47
|
+
if (item.index === totalCount - 1) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return <StyledBlueRightChevron />;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div>
|
|
55
|
+
<StyledHeaderSafeLink to={items[0]?.to}>{items[0]?.name}</StyledHeaderSafeLink>
|
|
56
|
+
<Breadcrumb
|
|
57
|
+
items={items.slice(1)}
|
|
58
|
+
renderItem={renderItem}
|
|
59
|
+
renderSeparator={renderSeparator}
|
|
60
|
+
autoCollapse
|
|
61
|
+
collapseFirst
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default HeaderBreadcrumb;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import styled from '@emotion/styled';
|
|
10
|
+
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
11
|
+
import { ChevronRight, Home } from '@ndla/icons/common';
|
|
12
|
+
import SafeLink from '@ndla/safelink';
|
|
13
|
+
import React from 'react';
|
|
14
|
+
import Breadcrumb from './Breadcrumb';
|
|
15
|
+
import { IndexedBreadcrumbItem, SimpleBreadcrumbItem } from './BreadcrumbItem';
|
|
16
|
+
|
|
17
|
+
interface ThemeProps {
|
|
18
|
+
light: boolean | undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const StyledSeparator = styled.div<ThemeProps>`
|
|
22
|
+
${fonts.sizes('14px')};
|
|
23
|
+
margin: 0 ${spacing.small};
|
|
24
|
+
user-select: none;
|
|
25
|
+
color: ${({ light }) => (light ? colors.white : colors.text.primary)};
|
|
26
|
+
${mq.range({ until: breakpoints.tablet })} {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const StyledIconSafeLink = styled(SafeLink)`
|
|
32
|
+
box-shadow: none;
|
|
33
|
+
border-bottom: none;
|
|
34
|
+
`;
|
|
35
|
+
const StyledHome = styled(Home)<ThemeProps>`
|
|
36
|
+
width: 20px;
|
|
37
|
+
height: 20px;
|
|
38
|
+
color: ${({ light }) => (light ? colors.white : colors.text.primary)};
|
|
39
|
+
`;
|
|
40
|
+
const StyledRightChevron = styled(ChevronRight)<ThemeProps>`
|
|
41
|
+
color: ${({ light }) => (light ? colors.white : colors.text.primary)};
|
|
42
|
+
margin: ${spacing.xxsmall};
|
|
43
|
+
`;
|
|
44
|
+
const StyledSpan = styled.span<ThemeProps>`
|
|
45
|
+
color: ${({ light }) => (light ? colors.white : colors.text.primary)};
|
|
46
|
+
`;
|
|
47
|
+
const StyledSafeLink = styled(SafeLink)<ThemeProps>`
|
|
48
|
+
color: ${({ light }) => (light ? colors.white : colors.text.primary)};
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
interface Props {
|
|
52
|
+
items: SimpleBreadcrumbItem[];
|
|
53
|
+
light?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const HomeBreadcrumb = ({ items, light }: Props) => {
|
|
57
|
+
const renderItem = (item: IndexedBreadcrumbItem, totalCount: number) => {
|
|
58
|
+
if (item.index === totalCount - 1) {
|
|
59
|
+
return <StyledSpan light={light}>{item.name}</StyledSpan>;
|
|
60
|
+
}
|
|
61
|
+
if (item.index === 0) {
|
|
62
|
+
return (
|
|
63
|
+
<StyledIconSafeLink aria-label={item.name} to={item.to}>
|
|
64
|
+
<StyledHome title={item.name} light={light} />
|
|
65
|
+
</StyledIconSafeLink>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return (
|
|
69
|
+
<StyledSafeLink light={light} to={item.to}>
|
|
70
|
+
{item.name}
|
|
71
|
+
</StyledSafeLink>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const renderSeparator = (item: IndexedBreadcrumbItem, totalCount: number) => {
|
|
76
|
+
if (item.index === totalCount - 1) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
if (item.index === 0) {
|
|
80
|
+
return <StyledSeparator light={light}>|</StyledSeparator>;
|
|
81
|
+
}
|
|
82
|
+
return <StyledRightChevron light={light} />;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return <Breadcrumb items={items} renderItem={renderItem} renderSeparator={renderSeparator} />;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default HomeBreadcrumb;
|
package/src/Breadcrumb/index.ts
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import Breadcrumb from './Breadcrumb';
|
|
10
10
|
|
|
11
|
-
export {
|
|
11
|
+
export type { SimpleBreadcrumbItem, IndexedBreadcrumbItem } from './BreadcrumbItem';
|
|
12
|
+
|
|
13
|
+
export { default as HeaderBreadcrumb } from './HeaderBreadcrumb';
|
|
14
|
+
|
|
15
|
+
export { default as HomeBreadcrumb } from './HomeBreadcrumb';
|
|
12
16
|
|
|
13
17
|
export default Breadcrumb;
|
package/src/index.ts
CHANGED
|
@@ -151,7 +151,8 @@ export type { TopicProps } from './Topic';
|
|
|
151
151
|
export { default as Aside } from './Aside';
|
|
152
152
|
export { default as AuthorInfo } from './AuthorInfo';
|
|
153
153
|
|
|
154
|
-
export { default as Breadcrumb,
|
|
154
|
+
export { default as Breadcrumb, HeaderBreadcrumb, HomeBreadcrumb } from './Breadcrumb';
|
|
155
|
+
export type { SimpleBreadcrumbItem, IndexedBreadcrumbItem } from './Breadcrumb';
|
|
155
156
|
|
|
156
157
|
export type { BreadcrumbItemProps } from './Breadcrumblist/Breadcrumblist';
|
|
157
158
|
export { i18nInstance, formatNestedMessages, formatMessage } from './i18n';
|
|
@@ -10,6 +10,8 @@ import { validateTranslationFiles } from '@ndla/util';
|
|
|
10
10
|
import messagesNB from '../messages-nb';
|
|
11
11
|
import messagesNN from '../messages-nn';
|
|
12
12
|
import messagesEN from '../messages-en';
|
|
13
|
+
import messagesSE from '../messages-se';
|
|
14
|
+
import messagesSMA from '../messages-sma';
|
|
13
15
|
|
|
14
16
|
test('That all translations has all language keys', () => {
|
|
15
17
|
const anyMissing = validateTranslationFiles(
|
|
@@ -26,6 +28,14 @@ test('That all translations has all language keys', () => {
|
|
|
26
28
|
languageName: 'English',
|
|
27
29
|
translationObject: messagesEN,
|
|
28
30
|
},
|
|
31
|
+
{
|
|
32
|
+
languageName: 'Nordsamisk',
|
|
33
|
+
translationObject: messagesSE,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
languageName: 'Sørsamisk',
|
|
37
|
+
translationObject: messagesSMA,
|
|
38
|
+
},
|
|
29
39
|
],
|
|
30
40
|
'only-on-error',
|
|
31
41
|
);
|