@ndla/ui 55.0.10-alpha.0 → 55.0.11-alpha.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.
Files changed (40) hide show
  1. package/es/index.js +0 -2
  2. package/lib/index.d.ts +0 -3
  3. package/lib/index.js +0 -20
  4. package/package.json +8 -8
  5. package/src/index.ts +0 -3
  6. package/es/MyNdla/Resource/Folder.js +0 -135
  7. package/es/MyNdla/Resource/index.js +0 -10
  8. package/es/MyNdla/index.js +0 -10
  9. package/es/Resource/BlockResource.js +0 -204
  10. package/es/Resource/ListResource.js +0 -235
  11. package/es/Resource/index.js +0 -10
  12. package/es/Resource/resourceComponents.js +0 -179
  13. package/es/Resource/storyComponents.js +0 -64
  14. package/lib/MyNdla/Resource/Folder.d.ts +0 -24
  15. package/lib/MyNdla/Resource/Folder.js +0 -142
  16. package/lib/MyNdla/Resource/index.d.ts +0 -9
  17. package/lib/MyNdla/Resource/index.js +0 -13
  18. package/lib/MyNdla/index.d.ts +0 -9
  19. package/lib/MyNdla/index.js +0 -13
  20. package/lib/Resource/BlockResource.d.ts +0 -28
  21. package/lib/Resource/BlockResource.js +0 -208
  22. package/lib/Resource/ListResource.d.ts +0 -28
  23. package/lib/Resource/ListResource.js +0 -239
  24. package/lib/Resource/index.d.ts +0 -11
  25. package/lib/Resource/index.js +0 -20
  26. package/lib/Resource/resourceComponents.d.ts +0 -43
  27. package/lib/Resource/resourceComponents.js +0 -188
  28. package/lib/Resource/storyComponents.d.ts +0 -12
  29. package/lib/Resource/storyComponents.js +0 -71
  30. package/src/MyNdla/Resource/Folder.stories.tsx +0 -87
  31. package/src/MyNdla/Resource/Folder.tsx +0 -228
  32. package/src/MyNdla/Resource/index.ts +0 -10
  33. package/src/MyNdla/index.ts +0 -10
  34. package/src/Resource/BlockResource.stories.tsx +0 -98
  35. package/src/Resource/BlockResource.tsx +0 -212
  36. package/src/Resource/ListResource.stories.tsx +0 -99
  37. package/src/Resource/ListResource.tsx +0 -273
  38. package/src/Resource/index.ts +0 -12
  39. package/src/Resource/resourceComponents.tsx +0 -242
  40. package/src/Resource/storyComponents.tsx +0 -49
@@ -1,273 +0,0 @@
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
- /** @jsxImportSource @emotion/react */
10
- import { ReactNode, useMemo } from "react";
11
- import styled from "@emotion/styled";
12
- import { spacing, colors, breakpoints, mq, stackOrder } from "@ndla/core";
13
- import { Text } from "@ndla/typography";
14
- import {
15
- CompressedTagList,
16
- ResourceImageProps,
17
- resourceHeadingStyle,
18
- ResourceTitleLink as StyledLink,
19
- ResourceTypeList,
20
- LoaderProps,
21
- ContentIconWrapper,
22
- } from "./resourceComponents";
23
- import ContentLoader from "../ContentLoader";
24
- import ContentTypeBadge from "../ContentTypeBadge";
25
- import Image from "../Image";
26
- import { contentTypeMapping, MISSING, resourceEmbedTypeMapping } from "../model/ContentType";
27
-
28
- const ListResourceWrapper = styled.div`
29
- flex: 1;
30
- display: grid;
31
- position: relative;
32
- grid-template-columns: auto minmax(50px, 1fr) auto;
33
- grid-template-areas:
34
- "image topicAndTitle tags"
35
- "image description description";
36
- ${mq.range({ until: breakpoints.mobileWide })} {
37
- grid-template-columns: auto 1fr;
38
- grid-template-areas:
39
- "image topicAndTitle"
40
- "description description"
41
- "tags tags";
42
- }
43
-
44
- cursor: pointer;
45
- border: 1px solid ${colors.brand.neutral7};
46
- border-radius: 2px;
47
-
48
- &:hover {
49
- box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
50
- transition-duration: 0.2s;
51
- [data-link] {
52
- color: ${colors.brand.primary};
53
- text-decoration: underline;
54
- }
55
- }
56
- `;
57
-
58
- interface StyledImageProps {
59
- imageSize: "normal" | "compact";
60
- }
61
-
62
- const ImageWrapper = styled.div<StyledImageProps>`
63
- grid-area: image;
64
- width: 56px;
65
- overflow: hidden;
66
- border-radius: 2px;
67
- display: flex;
68
- margin: ${spacing.small};
69
- align-items: center;
70
- justify-content: center;
71
- aspect-ratio: 4/3;
72
- &[data-image-size="normal"] {
73
- width: 136px;
74
- }
75
- ${mq.range({ until: breakpoints.mobileWide })} {
76
- width: 56px;
77
- margin-bottom: 0;
78
- }
79
- `;
80
-
81
- const StyledImage = styled(Image)`
82
- object-fit: cover;
83
- aspect-ratio: 4/3;
84
- `;
85
-
86
- const StyledResourceDescription = styled(Text)`
87
- grid-area: description;
88
- line-clamp: 2;
89
- height: 3.1em;
90
- margin: 0 ${spacing.small} ${spacing.small} 0;
91
- ${mq.range({ until: breakpoints.mobileWide })} {
92
- margin: 0 ${spacing.small};
93
- }
94
- overflow: hidden;
95
- text-overflow: ellipsis;
96
- // Unfortunate css needed for multi-line text overflow ellipsis.
97
- display: -webkit-box;
98
- -webkit-line-clamp: 2;
99
- line-clamp: 2;
100
- -webkit-box-orient: vertical;
101
- `;
102
-
103
- const TagsandActionMenu = styled.div`
104
- grid-area: tags;
105
- z-index: ${stackOrder.offsetSingle};
106
- box-sizing: content-box;
107
- display: grid;
108
- grid-template-columns: 1fr auto auto;
109
- align-items: center;
110
- align-self: flex-start;
111
- justify-items: flex-end;
112
- overflow: hidden;
113
- ${mq.range({ until: breakpoints.mobileWide })} {
114
- min-height: ${spacing.small};
115
- }
116
- `;
117
-
118
- const TopicAndTitleWrapper = styled.div`
119
- grid-area: topicAndTitle;
120
- display: flex;
121
- margin: ${spacing.small} 0;
122
- flex-direction: column;
123
- overflow: hidden;
124
- margin-right: ${spacing.small};
125
- ${mq.range({ until: breakpoints.mobileWide })} {
126
- margin-bottom: 0;
127
- }
128
- `;
129
-
130
- interface ListResourceImageProps {
131
- resourceImage: ResourceImageProps;
132
- loading?: boolean;
133
- type: "normal" | "compact";
134
- contentType: string;
135
- background?: boolean;
136
- }
137
-
138
- const ListResourceImage = ({ resourceImage, loading, type, contentType, background }: ListResourceImageProps) => {
139
- if (!loading) {
140
- if (resourceImage.src === "") {
141
- return (
142
- <ContentIconWrapper contentType={contentType}>
143
- <ContentTypeBadge type={contentType} background={background} size="x-small" />
144
- </ContentIconWrapper>
145
- );
146
- }
147
- return (
148
- <StyledImage alt={resourceImage.alt} src={resourceImage.src} fallbackWidth={type === "compact" ? 56 : 136} />
149
- );
150
- }
151
-
152
- return (
153
- <ContentLoader height={"100%"} width={"100%"} viewBox={null} preserveAspectRatio="none">
154
- <rect
155
- x="0"
156
- y="0"
157
- rx="3"
158
- ry="3"
159
- width={type === "compact" ? "56" : "136"}
160
- height={type === "compact" ? "40" : "96"}
161
- />
162
- </ContentLoader>
163
- );
164
- };
165
-
166
- const TypeAndTitleLoader = ({ loading, children }: LoaderProps) => {
167
- if (loading) {
168
- return (
169
- <ContentLoader height={"40px"} width={"100%"} viewBox={null} preserveAspectRatio="none">
170
- <rect x="0" y="0" rx="3" ry="3" width={"100%"} height={"16"} />
171
- <rect x="0" y="18" rx="3" ry="3" width={"70"} height={"16"} />
172
- <rect x="80" y="18" rx="3" ry="3" width={"70"} height={"16"} />
173
- </ContentLoader>
174
- );
175
- }
176
- return <>{children}</>;
177
- };
178
-
179
- interface ResourceDescriptionProps {
180
- description?: string;
181
- loading?: boolean;
182
- }
183
-
184
- const Description = ({ description, loading }: ResourceDescriptionProps) => {
185
- if (loading) {
186
- return (
187
- <ContentLoader height={"20px"} width={"100%"} viewBox={null} preserveAspectRatio="none">
188
- <rect x="0" y="0" width="100%" height="20" />
189
- </ContentLoader>
190
- );
191
- }
192
- return (
193
- <StyledResourceDescription element="p" textStyle="meta-text-small">
194
- {description}
195
- </StyledResourceDescription>
196
- );
197
- };
198
-
199
- export interface ListResourceProps {
200
- id: string;
201
- link: string;
202
- tagLinkPrefix?: string;
203
- title: string;
204
- resourceImage: ResourceImageProps;
205
- resourceTypes: { id: string; name: string }[];
206
- tags?: string[];
207
- description?: string;
208
- menu?: ReactNode;
209
- isLoading?: boolean;
210
- targetBlank?: boolean;
211
- }
212
-
213
- const ListResource = ({
214
- id,
215
- link,
216
- tagLinkPrefix,
217
- title,
218
- tags,
219
- resourceImage,
220
- resourceTypes,
221
- description,
222
- menu,
223
- isLoading = false,
224
- targetBlank,
225
- }: ListResourceProps) => {
226
- const showDescription = description !== undefined;
227
- const imageType = showDescription ? "normal" : "compact";
228
- const firstContentType = resourceTypes?.[0]?.id ?? "";
229
- const embedResourceType = resourceEmbedTypeMapping[firstContentType];
230
-
231
- const contentType = useMemo(() => {
232
- if (!firstContentType) {
233
- return MISSING;
234
- }
235
- return contentTypeMapping[firstContentType] ?? embedResourceType ?? contentTypeMapping.default;
236
- }, [embedResourceType, firstContentType]);
237
-
238
- return (
239
- <ListResourceWrapper id={id}>
240
- <ImageWrapper imageSize={imageType} data-image-size={imageType}>
241
- <ListResourceImage
242
- resourceImage={resourceImage}
243
- loading={isLoading}
244
- type={imageType}
245
- background={!!embedResourceType}
246
- contentType={contentType}
247
- />
248
- </ImageWrapper>
249
- <TopicAndTitleWrapper>
250
- <TypeAndTitleLoader loading={isLoading}>
251
- <StyledLink
252
- to={link}
253
- data-link=""
254
- target={targetBlank ? "_blank" : undefined}
255
- data-resource-available={contentType !== MISSING}
256
- >
257
- <Text element="span" textStyle="label-small" css={resourceHeadingStyle} title={title}>
258
- {title}
259
- </Text>
260
- </StyledLink>
261
- <ResourceTypeList resourceTypes={resourceTypes} />
262
- </TypeAndTitleLoader>
263
- </TopicAndTitleWrapper>
264
- {showDescription && <Description description={description} loading={isLoading} />}
265
- <TagsandActionMenu>
266
- {tags && tags.length > 0 && <CompressedTagList tagLinkPrefix={tagLinkPrefix} tags={tags} />}
267
- {menu}
268
- </TagsandActionMenu>
269
- </ListResourceWrapper>
270
- );
271
- };
272
-
273
- export default ListResource;
@@ -1,12 +0,0 @@
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 { ListResourceProps } from "./ListResource";
10
- export type { ListResourceProps };
11
- export { default as ListResource } from "./ListResource";
12
- export { default as BlockResource } from "./BlockResource";
@@ -1,242 +0,0 @@
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 { CSSProperties, HTMLAttributes, ReactNode, useMemo } from "react";
10
- import { useTranslation } from "react-i18next";
11
- import { css } from "@emotion/react";
12
- import styled from "@emotion/styled";
13
- import { IconButtonV2 } from "@ndla/button";
14
- import { colors, fonts, spacing, stackOrder } from "@ndla/core";
15
- import { DropdownMenu, DropdownContent, DropdownTrigger, DropdownItem } from "@ndla/dropdown-menu";
16
- import { HashTag } from "@ndla/icons/common";
17
- import { SafeLink, SafeLinkButton } from "@ndla/safelink";
18
- import { resourceEmbedTypeMapping } from "../model/ContentType";
19
- import resourceTypeColor from "../utils/resourceTypeColor";
20
-
21
- export interface ResourceImageProps {
22
- alt: string;
23
- src: string;
24
- }
25
-
26
- export const ResourceTitleLink = styled(SafeLink)`
27
- box-shadow: none;
28
- color: ${colors.brand.primary};
29
- flex: 1;
30
- &[data-resource-available="false"] {
31
- color: ${colors.brand.grey};
32
- font-style: italic;
33
- }
34
- :after {
35
- content: "";
36
- position: absolute;
37
- z-index: ${stackOrder.offsetSingle};
38
- top: 0;
39
- right: 0;
40
- bottom: 0;
41
- left: 0;
42
- }
43
- `;
44
-
45
- const StyledTrigger = styled(IconButtonV2)`
46
- margin: 0px ${spacing.xsmall};
47
- `;
48
-
49
- export const resourceHeadingStyle = css`
50
- margin: 0;
51
- overflow: hidden;
52
- text-overflow: ellipsis;
53
- // Unfortunate css needed for multi-line text overflow ellipsis.
54
- line-height: 1;
55
- display: -webkit-box;
56
- -webkit-line-clamp: 1;
57
- line-clamp: 1;
58
- -webkit-box-orient: vertical;
59
- grid-area: resourceTitle;
60
- `;
61
-
62
- const StyledTagList = styled.ul`
63
- list-style: none;
64
- display: flex;
65
- margin-left: ${spacing.small};
66
- padding: 2px;
67
- gap: ${spacing.xsmall};
68
- overflow: hidden;
69
- :last-child {
70
- margin-right: ${spacing.small};
71
- }
72
- `;
73
-
74
- const StyledTagListElement = styled.li`
75
- padding: 0;
76
- ${fonts.sizes(14)};
77
- `;
78
-
79
- const StyledSafeLink = styled(SafeLink)`
80
- display: flex;
81
- justify-content: flex-end;
82
- align-items: center;
83
- box-shadow: none;
84
- color: ${colors.brand.grey};
85
- min-height: 44px;
86
- min-width: 44px;
87
- white-space: nowrap;
88
- &:hover {
89
- color: ${colors.brand.primary};
90
- }
91
- `;
92
-
93
- const StyledResourceTypeList = styled.ul`
94
- list-style: none;
95
- display: flex;
96
- margin: 0;
97
- padding: 0;
98
- overflow: hidden;
99
- `;
100
-
101
- const StyledTopicDivider = styled.span`
102
- margin: 0;
103
- padding: 0 ${spacing.xxsmall};
104
- `;
105
-
106
- const StyledResourceListElement = styled.li`
107
- white-space: nowrap;
108
- ${fonts.sizes(12)};
109
- margin: 0;
110
- line-height: 1.5;
111
- padding: 0;
112
- display: flex;
113
- align-items: center;
114
- `;
115
-
116
- const TagCounterWrapper = styled.span`
117
- display: flex;
118
- font-weight: ${fonts.weight.semibold};
119
- ${fonts.sizes("14px", "14px")};
120
- `;
121
-
122
- interface ContentIconProps extends HTMLAttributes<HTMLSpanElement> {
123
- contentType: string;
124
- children?: ReactNode;
125
- }
126
-
127
- const StyledContentIconWrapper = styled.span`
128
- width: 100%;
129
- aspect-ratio: 4/3;
130
- display: flex;
131
- align-items: center;
132
- justify-content: center;
133
- background-color: var(--content-background-color);
134
- `;
135
-
136
- export const ContentIconWrapper = ({ contentType, children, ...props }: ContentIconProps) => {
137
- const contentIconWrapperVars = useMemo(
138
- () =>
139
- ({
140
- "--content-background-color": resourceTypeColor(contentType),
141
- }) as unknown as CSSProperties,
142
- [contentType],
143
- );
144
- return (
145
- <StyledContentIconWrapper {...props} style={contentIconWrapperVars}>
146
- {children}
147
- </StyledContentIconWrapper>
148
- );
149
- };
150
-
151
- interface TagListProps {
152
- tags?: string[];
153
- tagLinkPrefix?: string;
154
- }
155
-
156
- export interface LoaderProps {
157
- loading?: boolean;
158
- children?: ReactNode;
159
- }
160
-
161
- export const TagList = ({ tags, tagLinkPrefix }: TagListProps) => {
162
- const { t } = useTranslation();
163
- if (!tags) return null;
164
- return (
165
- <StyledTagList aria-label={t("myNdla.tagList")}>
166
- {tags.map((tag, i) => (
167
- <StyledTagListElement key={`tag-${i}`}>
168
- <StyledSafeLink to={`${tagLinkPrefix ? tagLinkPrefix : ""}/${encodeURIComponent(tag)}`}>
169
- <HashTag />
170
- {tag}
171
- </StyledSafeLink>
172
- </StyledTagListElement>
173
- ))}
174
- </StyledTagList>
175
- );
176
- };
177
-
178
- interface CompressedTagListProps {
179
- tags: string[];
180
- tagLinkPrefix?: string;
181
- }
182
-
183
- export const CompressedTagList = ({ tags, tagLinkPrefix }: CompressedTagListProps) => {
184
- const { t } = useTranslation();
185
- const visibleTags = useMemo(() => tags.slice(0, 3), [tags]);
186
- const remainingTags = useMemo(() => tags.slice(3, tags.length), [tags]);
187
-
188
- return (
189
- <>
190
- <TagList tagLinkPrefix={tagLinkPrefix} tags={visibleTags} />
191
- {remainingTags.length > 0 && (
192
- <DropdownMenu>
193
- <DropdownTrigger>
194
- <StyledTrigger
195
- size="xsmall"
196
- variant="ghost"
197
- colorTheme="light"
198
- aria-label={t("myNdla.moreTags", { count: remainingTags.length })}
199
- >
200
- {<TagCounterWrapper>{`+${remainingTags.length}`}</TagCounterWrapper>}
201
- </StyledTrigger>
202
- </DropdownTrigger>
203
- <DropdownContent showArrow>
204
- {remainingTags.map((tag, i) => (
205
- <DropdownItem key={`tag-${i}`}>
206
- <SafeLinkButton
207
- to={`${tagLinkPrefix ?? ""}/${encodeURIComponent(tag)}`}
208
- variant="ghost"
209
- colorTheme="light"
210
- >
211
- <HashTag />
212
- {tag}
213
- </SafeLinkButton>
214
- </DropdownItem>
215
- ))}
216
- </DropdownContent>
217
- </DropdownMenu>
218
- )}
219
- </>
220
- );
221
- };
222
-
223
- interface ResourceTypeListProps {
224
- resourceTypes?: { id: string; name: string }[];
225
- }
226
-
227
- export const ResourceTypeList = ({ resourceTypes }: ResourceTypeListProps) => {
228
- const { t } = useTranslation();
229
- if (!resourceTypes) return null;
230
- return (
231
- <StyledResourceTypeList aria-label={t("navigation.topics")}>
232
- {resourceTypes.map((resource, i) => (
233
- <StyledResourceListElement key={resource.id}>
234
- {resourceEmbedTypeMapping[resource.id]
235
- ? t(`embed.type.${resourceEmbedTypeMapping[resource.id]}`)
236
- : resource.name}
237
- {i !== resourceTypes.length - 1 && <StyledTopicDivider aria-hidden="true">•</StyledTopicDivider>}
238
- </StyledResourceListElement>
239
- ))}
240
- </StyledResourceTypeList>
241
- );
242
- };
@@ -1,49 +0,0 @@
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 { IconButtonV2, ButtonV2 } from "@ndla/button";
10
- import { DropdownMenu, DropdownTrigger, DropdownContent, DropdownItem } from "@ndla/dropdown-menu";
11
- import { Pencil } from "@ndla/icons/action";
12
- import { HorizontalMenu } from "@ndla/icons/contentType";
13
- import { DeleteForever } from "@ndla/icons/editor";
14
-
15
- export const resourceTypesArr = [
16
- { id: "urn:resourcetype:learningPath", name: "Læringssti" },
17
- { id: "urn:resourcetype:subjectMaterial", name: "Fagstoff" },
18
- {
19
- id: "urn:resourcetype:tasksAndActivities",
20
- name: "Oppgaver og aktiviteter",
21
- },
22
- { id: "urn:resourcetype:reviewResource", name: "Vurderingsressurs" },
23
- { id: "urn:resourcetype:externalResource", name: "Ekstern læringsressurs" },
24
- { id: "urn:resourcetype:SourceMaterial", name: "Kildemateriale" },
25
- ];
26
-
27
- export const StoryResourceMenu = () => (
28
- <DropdownMenu>
29
- <DropdownTrigger>
30
- <IconButtonV2 aria-label="Show more" title="Show more" variant="ghost" colorTheme="light">
31
- <HorizontalMenu />
32
- </IconButtonV2>
33
- </DropdownTrigger>
34
- <DropdownContent>
35
- <DropdownItem>
36
- <ButtonV2 variant="ghost" colorTheme="light" shape="sharp" size="small" fontWeight="normal">
37
- <Pencil />
38
- Rediger
39
- </ButtonV2>
40
- </DropdownItem>
41
- <DropdownItem>
42
- <ButtonV2 variant="ghost" colorTheme="danger" shape="sharp" size="small" fontWeight="normal">
43
- <DeleteForever />
44
- Slett
45
- </ButtonV2>
46
- </DropdownItem>
47
- </DropdownContent>
48
- </DropdownMenu>
49
- );