@kitconcept/volto-light-theme 6.0.0 → 7.0.0-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.
- package/.changelog.draft +10 -1
- package/CHANGELOG.md +17 -0
- package/package.json +2 -1
- package/src/components/Blocks/Teaser/DefaultBody.jsx +26 -39
- package/src/components/Card/Card.stories.tsx +251 -0
- package/src/components/Card/Card.tsx +143 -0
- package/src/components/Summary/DefaultSummary.tsx +24 -0
- package/src/components/Summary/{EventSummary.jsx → EventSummary.tsx} +5 -5
- package/src/components/Summary/{FileSummary.jsx → FileSummary.tsx} +4 -5
- package/src/components/Summary/{NewsItemSummary.jsx → NewsItemSummary.tsx} +5 -5
- package/src/components/Summary/Summary.stories.tsx +121 -0
- package/src/components/Widgets/ObjectList.tsx +2 -1
- package/src/stories/BlockWrapper.tsx +25 -0
- package/src/stories/grid.teaser.stories.tsx +156 -0
- package/src/stories/mocks.ts +504 -0
- package/src/stories/static/black-starry-night.jpg +0 -0
- package/src/stories/static/image-light.jpg +0 -0
- package/src/stories/teaser.stories.tsx +74 -0
- package/src/theme/_bgcolor-blocks-layout.scss +1 -1
- package/src/theme/_footer.scss +0 -1
- package/src/theme/_insets.scss +25 -0
- package/src/theme/_layout.scss +30 -1
- package/src/theme/blocks/_grid.scss +14 -1
- package/src/theme/blocks/_teaser.scss +15 -2
- package/src/theme/card.scss +168 -0
- package/src/theme/main.scss +2 -0
- package/src/components/Summary/DefaultSummary.jsx +0 -16
package/.changelog.draft
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
##
|
|
1
|
+
## 7.0.0-alpha.0 (2025-05-16)
|
|
2
|
+
|
|
3
|
+
### Breaking
|
|
4
|
+
|
|
5
|
+
- The new card primitive has been applied to all Teasers. @sneridagh
|
|
6
|
+
See upgrade guide for more information. [#537](https://github.com/kitconcept/volto-light-theme/pull/537)
|
|
7
|
+
|
|
8
|
+
### Feature
|
|
9
|
+
|
|
10
|
+
- Added new card primitive. @sneridagh [#537](https://github.com/kitconcept/volto-light-theme/pull/537)
|
|
2
11
|
|
|
3
12
|
|
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,23 @@
|
|
|
8
8
|
|
|
9
9
|
<!-- towncrier release notes start -->
|
|
10
10
|
|
|
11
|
+
## 7.0.0-alpha.0 (2025-05-16)
|
|
12
|
+
|
|
13
|
+
### Breaking
|
|
14
|
+
|
|
15
|
+
- The new card primitive has been applied to all Teasers. @sneridagh
|
|
16
|
+
See upgrade guide for more information. [#537](https://github.com/kitconcept/volto-light-theme/pull/537)
|
|
17
|
+
|
|
18
|
+
### Feature
|
|
19
|
+
|
|
20
|
+
- Added new card primitive. @sneridagh [#537](https://github.com/kitconcept/volto-light-theme/pull/537)
|
|
21
|
+
|
|
22
|
+
## 6.0.1 (2025-05-15)
|
|
23
|
+
|
|
24
|
+
### Bugfix
|
|
25
|
+
|
|
26
|
+
- Removed background color specification in the whole footer. We only want it in some areas of it, and depending if some sections are present or not. @sneridagh
|
|
27
|
+
|
|
11
28
|
## 6.0.0 (2025-05-14)
|
|
12
29
|
|
|
13
30
|
## 6.0.0-alpha.25 (2025-05-14)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kitconcept/volto-light-theme",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-alpha.0",
|
|
4
4
|
"description": "Volto Light Theme by kitconcept",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@plone/scripts": "^3.6.2",
|
|
31
|
+
"@storybook/react": "^8.6.12",
|
|
31
32
|
"@types/jest": "^29.5.8",
|
|
32
33
|
"@types/lodash": "^4.14.201",
|
|
33
34
|
"@types/react": "^18.3.12",
|
|
@@ -4,11 +4,11 @@ import { Message } from 'semantic-ui-react';
|
|
|
4
4
|
import { defineMessages, useIntl } from 'react-intl';
|
|
5
5
|
import imageBlockSVG from '@plone/volto/components/manage/Blocks/Image/block-image.svg';
|
|
6
6
|
import { isInternalURL } from '@plone/volto/helpers/Url/Url';
|
|
7
|
-
import MaybeWrap from '@plone/volto/components/manage/MaybeWrap/MaybeWrap';
|
|
8
|
-
import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
|
|
9
7
|
import cx from 'classnames';
|
|
10
8
|
import config from '@plone/volto/registry';
|
|
11
9
|
import DefaultSummary from '@kitconcept/volto-light-theme/components/Summary/DefaultSummary';
|
|
10
|
+
import Card from '../../Card/Card';
|
|
11
|
+
import isEmpty from 'lodash/isEmpty';
|
|
12
12
|
|
|
13
13
|
const messages = defineMessages({
|
|
14
14
|
PleaseChooseContent: {
|
|
@@ -32,11 +32,17 @@ const TeaserDefaultTemplate = (props) => {
|
|
|
32
32
|
dependencies: [href['@type']],
|
|
33
33
|
}).component || DefaultSummary;
|
|
34
34
|
const { openExternalLinkInNewTab } = config.settings;
|
|
35
|
+
const openLinkInNewTab =
|
|
36
|
+
data.openLinkInNewTab ||
|
|
37
|
+
(openExternalLinkInNewTab && !isInternalURL(href['@id']))
|
|
38
|
+
? '_blank'
|
|
39
|
+
: null;
|
|
40
|
+
const { '@id': id, ...filteredData } = data;
|
|
35
41
|
|
|
36
42
|
return (
|
|
37
43
|
<div className={cx('block teaser', className)} style={style}>
|
|
38
44
|
<>
|
|
39
|
-
{
|
|
45
|
+
{isEmpty(href) && isEditMode && (
|
|
40
46
|
<Message>
|
|
41
47
|
<div className="teaser-item placeholder">
|
|
42
48
|
<img src={imageBlockSVG} alt="" />
|
|
@@ -44,42 +50,23 @@ const TeaserDefaultTemplate = (props) => {
|
|
|
44
50
|
</div>
|
|
45
51
|
</Message>
|
|
46
52
|
)}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
) : (
|
|
65
|
-
(href.hasPreviewImage || href.image_field || image) && (
|
|
66
|
-
<div className="image-wrapper">
|
|
67
|
-
<Image
|
|
68
|
-
item={image || href}
|
|
69
|
-
imageField={image ? image.image_field : href.image_field}
|
|
70
|
-
alt=""
|
|
71
|
-
loading="lazy"
|
|
72
|
-
responsive={true}
|
|
73
|
-
/>
|
|
74
|
-
</div>
|
|
75
|
-
)
|
|
76
|
-
)}
|
|
77
|
-
<div className="content">
|
|
78
|
-
<Summary item={{ ...href, ...data }} HeadingTag="h2" />
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
</MaybeWrap>
|
|
82
|
-
)}
|
|
53
|
+
<Card
|
|
54
|
+
href={!isEditMode ? href['@id'] : null}
|
|
55
|
+
openLinkInNewTab={openLinkInNewTab}
|
|
56
|
+
>
|
|
57
|
+
<Card.Image
|
|
58
|
+
src={url && !image?.image_field ? url : undefined}
|
|
59
|
+
item={!data.overwrite ? href : { ...href, ...filteredData }}
|
|
60
|
+
image={data.overwrite ? image : undefined}
|
|
61
|
+
imageComponent={Image}
|
|
62
|
+
/>
|
|
63
|
+
<Card.Summary>
|
|
64
|
+
<Summary
|
|
65
|
+
item={!data.overwrite ? href : { ...href, ...filteredData }}
|
|
66
|
+
HeadingTag="h2"
|
|
67
|
+
/>
|
|
68
|
+
</Card.Summary>
|
|
69
|
+
</Card>
|
|
83
70
|
</>
|
|
84
71
|
</div>
|
|
85
72
|
);
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Card from './Card';
|
|
3
|
+
|
|
4
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
5
|
+
import { ObjectBrowserItem } from '../../stories/mocks';
|
|
6
|
+
import Wrapper from '@plone/volto/storybook';
|
|
7
|
+
import { Button } from '@plone/components';
|
|
8
|
+
import DefaultSummary from '../Summary/DefaultSummary';
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
title: 'Primitives/Card',
|
|
12
|
+
component: Card,
|
|
13
|
+
parameters: {
|
|
14
|
+
layout: 'centered',
|
|
15
|
+
// backgrounds: { disable: true },
|
|
16
|
+
},
|
|
17
|
+
decorators: [
|
|
18
|
+
(Story) => (
|
|
19
|
+
<Wrapper>
|
|
20
|
+
<Story />
|
|
21
|
+
</Wrapper>
|
|
22
|
+
),
|
|
23
|
+
],
|
|
24
|
+
tags: ['autodocs'],
|
|
25
|
+
} satisfies Meta<typeof Card>;
|
|
26
|
+
|
|
27
|
+
export default meta;
|
|
28
|
+
type Story = StoryObj<typeof meta>;
|
|
29
|
+
|
|
30
|
+
const item = {
|
|
31
|
+
title: 'Card Title',
|
|
32
|
+
description:
|
|
33
|
+
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea.',
|
|
34
|
+
head_title: 'Card Kicker',
|
|
35
|
+
};
|
|
36
|
+
const imageSRC = 'black-starry-night.jpg';
|
|
37
|
+
const imageSRC2 = 'image-light.jpg';
|
|
38
|
+
|
|
39
|
+
export const Simple: Story = {
|
|
40
|
+
render: (args) => (
|
|
41
|
+
<Card href={args.href}>
|
|
42
|
+
<Card.Image src={imageSRC} />
|
|
43
|
+
<Card.Summary>
|
|
44
|
+
<DefaultSummary item={item} HeadingTag="h2" />
|
|
45
|
+
</Card.Summary>
|
|
46
|
+
</Card>
|
|
47
|
+
),
|
|
48
|
+
args: {
|
|
49
|
+
href: '/folder/page',
|
|
50
|
+
},
|
|
51
|
+
decorators: [
|
|
52
|
+
(Story) => (
|
|
53
|
+
<div style={{ width: '300px' }}>
|
|
54
|
+
<Story />
|
|
55
|
+
</div>
|
|
56
|
+
),
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const SimpleWithoutLink: Story = {
|
|
61
|
+
render: (args) => (
|
|
62
|
+
<Card>
|
|
63
|
+
<Card.Image src={imageSRC} />
|
|
64
|
+
<Card.Summary>
|
|
65
|
+
<DefaultSummary item={item} HeadingTag="h2" />
|
|
66
|
+
</Card.Summary>
|
|
67
|
+
</Card>
|
|
68
|
+
),
|
|
69
|
+
args: {
|
|
70
|
+
href: '/folder/page',
|
|
71
|
+
},
|
|
72
|
+
decorators: [
|
|
73
|
+
(Story) => (
|
|
74
|
+
<div style={{ width: '300px' }}>
|
|
75
|
+
<Story />
|
|
76
|
+
</div>
|
|
77
|
+
),
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const AlignedLeft: Story = {
|
|
82
|
+
render: (args) => (
|
|
83
|
+
<div
|
|
84
|
+
className="has--align--left"
|
|
85
|
+
style={{ width: 'var(--default-container-width)' }}
|
|
86
|
+
>
|
|
87
|
+
<Card href={args.href}>
|
|
88
|
+
<Card.Image src={imageSRC} />
|
|
89
|
+
<Card.Summary>
|
|
90
|
+
<DefaultSummary item={item} HeadingTag="h2" />
|
|
91
|
+
</Card.Summary>
|
|
92
|
+
</Card>
|
|
93
|
+
</div>
|
|
94
|
+
),
|
|
95
|
+
args: {
|
|
96
|
+
href: '/folder/page',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const AlignedRight: Story = {
|
|
101
|
+
render: (args) => (
|
|
102
|
+
<div
|
|
103
|
+
className="has--align--right"
|
|
104
|
+
style={{ width: 'var(--default-container-width)' }}
|
|
105
|
+
>
|
|
106
|
+
<Card href={args.href}>
|
|
107
|
+
<Card.Image src={imageSRC} />
|
|
108
|
+
<Card.Summary>
|
|
109
|
+
<DefaultSummary item={item} HeadingTag="h2" />
|
|
110
|
+
</Card.Summary>
|
|
111
|
+
</Card>
|
|
112
|
+
</div>
|
|
113
|
+
),
|
|
114
|
+
args: {
|
|
115
|
+
href: '/folder/page',
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const SimpleContained: Story = {
|
|
120
|
+
render: (args) => (
|
|
121
|
+
<div
|
|
122
|
+
className="contained"
|
|
123
|
+
style={{ ['--theme-high-contrast-color' as string]: '#ecebeb' }}
|
|
124
|
+
>
|
|
125
|
+
<Card href={args.href}>
|
|
126
|
+
<Card.Image src={imageSRC} />
|
|
127
|
+
<Card.Summary>
|
|
128
|
+
<DefaultSummary item={item} HeadingTag="h2" />
|
|
129
|
+
</Card.Summary>
|
|
130
|
+
</Card>
|
|
131
|
+
</div>
|
|
132
|
+
),
|
|
133
|
+
args: {
|
|
134
|
+
href: '/folder/page',
|
|
135
|
+
},
|
|
136
|
+
decorators: [
|
|
137
|
+
(Story) => (
|
|
138
|
+
<div style={{ width: '300px' }}>
|
|
139
|
+
<Story />
|
|
140
|
+
</div>
|
|
141
|
+
),
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const SimpleListing: Story = {
|
|
146
|
+
render: (args) => (
|
|
147
|
+
<div
|
|
148
|
+
className="card-listing"
|
|
149
|
+
style={{ width: 'var(--default-container-width)' }}
|
|
150
|
+
>
|
|
151
|
+
<Card href={args.href}>
|
|
152
|
+
<Card.Image src={imageSRC} />
|
|
153
|
+
<Card.Summary>
|
|
154
|
+
<DefaultSummary item={item} HeadingTag="h2" />
|
|
155
|
+
</Card.Summary>
|
|
156
|
+
</Card>
|
|
157
|
+
</div>
|
|
158
|
+
),
|
|
159
|
+
args: {
|
|
160
|
+
href: '/folder/page',
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const CustomImage: Story = {
|
|
165
|
+
render: (args) => (
|
|
166
|
+
<div
|
|
167
|
+
className="card-listing"
|
|
168
|
+
style={{ width: 'var(--default-container-width)' }}
|
|
169
|
+
>
|
|
170
|
+
<Card href={args.href}>
|
|
171
|
+
<Card.Image>
|
|
172
|
+
<div className="date-inset">
|
|
173
|
+
<div className="day">10</div>
|
|
174
|
+
<div className="month">May 2025</div>
|
|
175
|
+
</div>
|
|
176
|
+
</Card.Image>
|
|
177
|
+
<Card.Summary>
|
|
178
|
+
<DefaultSummary item={item} HeadingTag="h2" />
|
|
179
|
+
</Card.Summary>
|
|
180
|
+
</Card>
|
|
181
|
+
</div>
|
|
182
|
+
),
|
|
183
|
+
args: {
|
|
184
|
+
href: '/folder/page',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export const CustomAction: Story = {
|
|
189
|
+
render: (args) => (
|
|
190
|
+
<div style={{ width: 'var(--default-container-width)' }}>
|
|
191
|
+
<Card href={args.href}>
|
|
192
|
+
<Card.Image src={imageSRC} />
|
|
193
|
+
<Card.Summary>
|
|
194
|
+
<DefaultSummary item={item} HeadingTag="h2" />
|
|
195
|
+
</Card.Summary>
|
|
196
|
+
<Card.Actions>
|
|
197
|
+
<div style={{ marginTop: '20px' }}>
|
|
198
|
+
<a href="https://www.plone.org" rel="noreferrer" target="_blank">
|
|
199
|
+
<Button>Read More</Button>
|
|
200
|
+
</a>
|
|
201
|
+
</div>
|
|
202
|
+
</Card.Actions>
|
|
203
|
+
</Card>
|
|
204
|
+
</div>
|
|
205
|
+
),
|
|
206
|
+
args: {
|
|
207
|
+
href: '/folder/page',
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const WithItem: Story = {
|
|
212
|
+
render: (args) => (
|
|
213
|
+
<Card href={args.href}>
|
|
214
|
+
<Card.Image src={imageSRC} />
|
|
215
|
+
<Card.Summary>
|
|
216
|
+
<DefaultSummary item={ObjectBrowserItem(imageSRC)} HeadingTag="h2" />
|
|
217
|
+
</Card.Summary>
|
|
218
|
+
</Card>
|
|
219
|
+
),
|
|
220
|
+
args: {
|
|
221
|
+
href: ObjectBrowserItem(imageSRC)['@id'],
|
|
222
|
+
},
|
|
223
|
+
decorators: [
|
|
224
|
+
(Story) => (
|
|
225
|
+
<div style={{ width: '300px' }}>
|
|
226
|
+
<Story />
|
|
227
|
+
</div>
|
|
228
|
+
),
|
|
229
|
+
],
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export const WithCustomImage: Story = {
|
|
233
|
+
render: (args) => (
|
|
234
|
+
<Card href={args.href}>
|
|
235
|
+
<Card.Image image={ObjectBrowserItem(imageSRC2)} />
|
|
236
|
+
<Card.Summary>
|
|
237
|
+
<DefaultSummary item={ObjectBrowserItem(imageSRC)} HeadingTag="h2" />
|
|
238
|
+
</Card.Summary>
|
|
239
|
+
</Card>
|
|
240
|
+
),
|
|
241
|
+
args: {
|
|
242
|
+
href: ObjectBrowserItem(imageSRC)['@id'],
|
|
243
|
+
},
|
|
244
|
+
decorators: [
|
|
245
|
+
(Story) => (
|
|
246
|
+
<div style={{ width: '300px' }}>
|
|
247
|
+
<Story />
|
|
248
|
+
</div>
|
|
249
|
+
),
|
|
250
|
+
],
|
|
251
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import ConditionalLink from '@plone/volto/components/manage/ConditionalLink/ConditionalLink';
|
|
3
|
+
import cx from 'classnames';
|
|
4
|
+
import type { ObjectBrowserItem } from '@plone/types';
|
|
5
|
+
|
|
6
|
+
type CardProps = {
|
|
7
|
+
/** Optional additional CSS class names to apply to the card. */
|
|
8
|
+
className?: string;
|
|
9
|
+
/** Optional URL to make the card clickable as a link. */
|
|
10
|
+
href?: string;
|
|
11
|
+
/** If true and `href` is provided, opens the link in a new browser tab. */
|
|
12
|
+
openLinkInNewTab?: boolean;
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const DefaultImage = (props: any) => {
|
|
17
|
+
const { src, item, imageField, alt, loading, responsive } = props;
|
|
18
|
+
return (
|
|
19
|
+
<img
|
|
20
|
+
src={src || item?.image_scales?.[imageField]?.[0].download}
|
|
21
|
+
alt={alt}
|
|
22
|
+
loading={loading}
|
|
23
|
+
className={responsive ? 'responsive' : ''}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const childrenWithProps = (children, extraProps) => {
|
|
29
|
+
return React.Children.map(children, (child) => {
|
|
30
|
+
if (React.isValidElement(child)) {
|
|
31
|
+
return React.cloneElement(child, extraProps);
|
|
32
|
+
}
|
|
33
|
+
return child;
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const Card = (props: CardProps) => {
|
|
38
|
+
const { className, href, openLinkInNewTab } = props;
|
|
39
|
+
|
|
40
|
+
const a11yLabelId = React.useId();
|
|
41
|
+
const linkRef = React.useRef<HTMLAnchorElement>(null);
|
|
42
|
+
|
|
43
|
+
const triggerNavigation = () => {
|
|
44
|
+
// Only navigate if there is *no* text selection
|
|
45
|
+
const hasSelection = !!window.getSelection()?.toString();
|
|
46
|
+
if (!hasSelection) {
|
|
47
|
+
linkRef.current?.click();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const onClick: React.MouseEventHandler<HTMLDivElement> = () => {
|
|
52
|
+
if (href) triggerNavigation();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (e) => {
|
|
56
|
+
if (!href) return;
|
|
57
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
triggerNavigation();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div
|
|
65
|
+
className={cx('card', className)}
|
|
66
|
+
onClick={onClick}
|
|
67
|
+
onKeyDown={onKeyDown}
|
|
68
|
+
role={href ? 'link' : undefined}
|
|
69
|
+
tabIndex={href ? 0 : undefined}
|
|
70
|
+
>
|
|
71
|
+
{/* @ts-expect-error since this has no children, should fail */}
|
|
72
|
+
<ConditionalLink
|
|
73
|
+
aria-labelledby={a11yLabelId}
|
|
74
|
+
condition={!!href}
|
|
75
|
+
href={href}
|
|
76
|
+
openLinkInNewTab={openLinkInNewTab}
|
|
77
|
+
ref={linkRef}
|
|
78
|
+
/>
|
|
79
|
+
<div className="card-inner">
|
|
80
|
+
{childrenWithProps(props.children, { a11yLabelId })}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
type CardImageProps = {
|
|
87
|
+
/** The source URL of the image to display. */
|
|
88
|
+
src?: string;
|
|
89
|
+
/** An optional item object, used to provide image data from the current item. */
|
|
90
|
+
item?: Partial<ObjectBrowserItem>;
|
|
91
|
+
/** An optional image object, used as an alternative source of image data for the item. */
|
|
92
|
+
image?: Partial<ObjectBrowserItem>;
|
|
93
|
+
/** A custom React component to render the image. */
|
|
94
|
+
imageComponent?: React.ComponentType<any>;
|
|
95
|
+
children?: React.ReactNode;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const CardImage = (props: CardImageProps) => {
|
|
99
|
+
const { src, item, image, imageComponent } = props;
|
|
100
|
+
const Image = imageComponent || DefaultImage;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className="image-wrapper">
|
|
104
|
+
{src ? (
|
|
105
|
+
<Image src={src} alt="" loading="lazy" responsive={true} />
|
|
106
|
+
) : item || image ? (
|
|
107
|
+
(item?.hasPreviewImage || item?.image_field || image) && (
|
|
108
|
+
<Image
|
|
109
|
+
item={image || item}
|
|
110
|
+
imageField={image ? image.image_field : item?.image_field}
|
|
111
|
+
alt=""
|
|
112
|
+
loading="lazy"
|
|
113
|
+
responsive={true}
|
|
114
|
+
/>
|
|
115
|
+
)
|
|
116
|
+
) : (
|
|
117
|
+
props.children
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
type CardSummaryProps = {
|
|
124
|
+
/** The ID of the element that labels the card. */
|
|
125
|
+
a11yLabelId?: string;
|
|
126
|
+
children?: React.ReactNode;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const CardSummary = (props: CardSummaryProps) => (
|
|
130
|
+
<div className="card-summary">
|
|
131
|
+
{childrenWithProps(props.children, { a11yLabelId: props.a11yLabelId })}
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const CardActions = (props: any) => (
|
|
136
|
+
<div className="actions-wrapper">{props.children}</div>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
Card.Image = CardImage;
|
|
140
|
+
Card.Summary = CardSummary;
|
|
141
|
+
Card.Actions = CardActions;
|
|
142
|
+
|
|
143
|
+
export default Card;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ObjectBrowserItem } from '@plone/types';
|
|
3
|
+
|
|
4
|
+
export type DefaultSummaryProps = {
|
|
5
|
+
item: Partial<ObjectBrowserItem>;
|
|
6
|
+
HeadingTag?: React.ElementType;
|
|
7
|
+
a11yLabelId?: string;
|
|
8
|
+
hide_description?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DefaultSummary = (props: DefaultSummaryProps) => {
|
|
12
|
+
const { item, HeadingTag = 'h3', a11yLabelId, hide_description } = props;
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
{item?.head_title && <div className="headline">{item.head_title}</div>}
|
|
16
|
+
<HeadingTag className="title" id={a11yLabelId}>
|
|
17
|
+
{item.title ? item.title : item.id}
|
|
18
|
+
</HeadingTag>
|
|
19
|
+
{!hide_description && <p className="description">{item.description}</p>}
|
|
20
|
+
</>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default DefaultSummary;
|
|
@@ -3,9 +3,10 @@ import {
|
|
|
3
3
|
formatDateRange,
|
|
4
4
|
} from '@kitconcept/volto-light-theme/helpers/dates';
|
|
5
5
|
import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
|
|
6
|
+
import type { DefaultSummaryProps } from './DefaultSummary';
|
|
6
7
|
|
|
7
|
-
const EventSummary = (props) => {
|
|
8
|
-
const { item, HeadingTag = 'h3' } = props;
|
|
8
|
+
const EventSummary = (props: DefaultSummaryProps) => {
|
|
9
|
+
const { item, HeadingTag = 'h3', hide_description } = props;
|
|
9
10
|
const start = parseDateFromCatalog(item.start);
|
|
10
11
|
const end = parseDateFromCatalog(item.end);
|
|
11
12
|
const headline = [
|
|
@@ -14,6 +15,7 @@ const EventSummary = (props) => {
|
|
|
14
15
|
{formatDateRange({ start, end, locale: item.Language })}
|
|
15
16
|
</span>
|
|
16
17
|
) : start ? (
|
|
18
|
+
// @ts-expect-error
|
|
17
19
|
<FormattedDate key="day" date={start} />
|
|
18
20
|
) : null,
|
|
19
21
|
item.head_title,
|
|
@@ -28,9 +30,7 @@ const EventSummary = (props) => {
|
|
|
28
30
|
<HeadingTag className="title">
|
|
29
31
|
{item.title ? item.title : item.id}
|
|
30
32
|
</HeadingTag>
|
|
31
|
-
{!
|
|
32
|
-
<p className="description">{item.description}</p>
|
|
33
|
-
)}
|
|
33
|
+
{!hide_description && <p className="description">{item.description}</p>}
|
|
34
34
|
</>
|
|
35
35
|
);
|
|
36
36
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import FileType from '@kitconcept/volto-light-theme/helpers/Filetype';
|
|
2
|
+
import type { DefaultSummaryProps } from './DefaultSummary';
|
|
2
3
|
|
|
3
|
-
const FileSummary = (props) => {
|
|
4
|
-
const { item, HeadingTag = 'h3' } = props;
|
|
4
|
+
const FileSummary = (props: DefaultSummaryProps) => {
|
|
5
|
+
const { item, HeadingTag = 'h3', hide_description } = props;
|
|
5
6
|
|
|
6
7
|
const headline = [item.getObjSize, FileType(item.mime_type), item.head_title]
|
|
7
8
|
.filter((x) => x)
|
|
@@ -14,9 +15,7 @@ const FileSummary = (props) => {
|
|
|
14
15
|
<HeadingTag className="title">
|
|
15
16
|
{item.title ? item.title : item.id}
|
|
16
17
|
</HeadingTag>
|
|
17
|
-
{!
|
|
18
|
-
<p className="description">{item.description}</p>
|
|
19
|
-
)}
|
|
18
|
+
{!hide_description && <p className="description">{item.description}</p>}
|
|
20
19
|
</>
|
|
21
20
|
);
|
|
22
21
|
};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { parseDateFromCatalog } from '@kitconcept/volto-light-theme/helpers/dates';
|
|
2
2
|
import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
|
|
3
|
+
import type { DefaultSummaryProps } from './DefaultSummary';
|
|
3
4
|
|
|
4
|
-
const NewsItemSummary = (props) => {
|
|
5
|
-
const { item, HeadingTag = 'h3' } = props;
|
|
5
|
+
const NewsItemSummary = (props: DefaultSummaryProps) => {
|
|
6
|
+
const { item, HeadingTag = 'h3', hide_description } = props;
|
|
6
7
|
|
|
7
8
|
const effective = parseDateFromCatalog(item.effective);
|
|
8
9
|
const headline = [
|
|
9
10
|
effective ? (
|
|
11
|
+
// @ts-expect-error
|
|
10
12
|
<FormattedDate
|
|
11
13
|
key="day"
|
|
12
14
|
date={effective}
|
|
@@ -30,9 +32,7 @@ const NewsItemSummary = (props) => {
|
|
|
30
32
|
<HeadingTag className="title">
|
|
31
33
|
{item.title ? item.title : item.id}
|
|
32
34
|
</HeadingTag>
|
|
33
|
-
{!
|
|
34
|
-
<p className="description">{item.description}</p>
|
|
35
|
-
)}
|
|
35
|
+
{!hide_description && <p className="description">{item.description}</p>}
|
|
36
36
|
</>
|
|
37
37
|
);
|
|
38
38
|
};
|