@ossy/design-system-extras 0.0.1-beta

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 (64) hide show
  1. package/.storybook/main.js +19 -0
  2. package/.storybook/preview.jsx +35 -0
  3. package/build/cjs/index.js +1 -0
  4. package/build/esm/index.js +1 -0
  5. package/package.json +75 -0
  6. package/rollup.config.js +35 -0
  7. package/src/index.js +3 -0
  8. package/src/markdown-viewer/MarkdownViewer.jsx +86 -0
  9. package/src/markdown-viewer/MarkdownViewer.stories.jsx +34 -0
  10. package/src/markdown-viewer/index.js +1 -0
  11. package/src/markdown-viewer/styles/atom-one-light.js +114 -0
  12. package/src/router-layouts/Footer.jsx +38 -0
  13. package/src/router-layouts/StandardLayout.jsx +95 -0
  14. package/src/router-layouts/StandardLayout.stories.jsx +40 -0
  15. package/src/router-layouts/card/Card.layout.jsx +25 -0
  16. package/src/router-layouts/card/Card.layout.stories.jsx +15 -0
  17. package/src/router-layouts/card/index.js +1 -0
  18. package/src/router-layouts/card-sidebar/CardSidebar.jsx +48 -0
  19. package/src/router-layouts/card-sidebar/CardSidebar.stories.jsx +23 -0
  20. package/src/router-layouts/card-sidebar/index.js +1 -0
  21. package/src/router-layouts/card-tabs/CardTabs.jsx +35 -0
  22. package/src/router-layouts/card-tabs/CardTabs.stories.jsx +22 -0
  23. package/src/router-layouts/card-tabs/index.js +1 -0
  24. package/src/router-layouts/index.js +4 -0
  25. package/src/stories/Button.jsx +39 -0
  26. package/src/stories/Button.stories.js +49 -0
  27. package/src/stories/Configure.mdx +364 -0
  28. package/src/stories/Header.jsx +56 -0
  29. package/src/stories/Header.stories.js +29 -0
  30. package/src/stories/Page.jsx +69 -0
  31. package/src/stories/Page.stories.js +28 -0
  32. package/src/stories/assets/accessibility.png +0 -0
  33. package/src/stories/assets/accessibility.svg +1 -0
  34. package/src/stories/assets/addon-library.png +0 -0
  35. package/src/stories/assets/assets.png +0 -0
  36. package/src/stories/assets/avif-test-image.avif +0 -0
  37. package/src/stories/assets/context.png +0 -0
  38. package/src/stories/assets/discord.svg +1 -0
  39. package/src/stories/assets/docs.png +0 -0
  40. package/src/stories/assets/figma-plugin.png +0 -0
  41. package/src/stories/assets/github.svg +1 -0
  42. package/src/stories/assets/share.png +0 -0
  43. package/src/stories/assets/styling.png +0 -0
  44. package/src/stories/assets/testing.png +0 -0
  45. package/src/stories/assets/theming.png +0 -0
  46. package/src/stories/assets/tutorials.svg +1 -0
  47. package/src/stories/assets/youtube.svg +1 -0
  48. package/src/stories/button.css +30 -0
  49. package/src/stories/header.css +32 -0
  50. package/src/stories/page.css +68 -0
  51. package/src/templates/index.js +3 -0
  52. package/src/templates/landing-page/2020/Footer.jsx +59 -0
  53. package/src/templates/landing-page/2020/Hero.jsx +51 -0
  54. package/src/templates/landing-page/2020/LandingPage.jsx +180 -0
  55. package/src/templates/landing-page/2020/LandingPage.stories.jsx +190 -0
  56. package/src/templates/landing-page/2020/index.js +1 -0
  57. package/src/templates/landing-page/index.js +1 -0
  58. package/src/templates/resume/Resume.jsx +305 -0
  59. package/src/templates/resume/Resume.stories.jsx +237 -0
  60. package/src/templates/resume/index.js +2 -0
  61. package/src/templates/resume/useResume.js +85 -0
  62. package/src/templates/theme-display/ThemeDisplay.jsx +301 -0
  63. package/src/templates/theme-display/ThemeDisplay.stories.jsx +18 -0
  64. package/src/templates/theme-display/index.js +1 -0
@@ -0,0 +1,305 @@
1
+ import React from 'react'
2
+ import {
3
+ Button,
4
+ Title,
5
+ Dropdown,
6
+ ContextMenu,
7
+ Tags,
8
+ Tabs,
9
+ ProfileCard,
10
+ ResumeExperience,
11
+ } from "@ossy/design-system"
12
+ import { useResume } from './useResume'
13
+
14
+ export const Resume = ({
15
+ status,
16
+ profile,
17
+ experiences,
18
+ translations,
19
+ profileCardProps = { },
20
+ actions = []
21
+ }) => {
22
+
23
+ const {
24
+ work,
25
+ education,
26
+ project,
27
+ other,
28
+ tags,
29
+ categories,
30
+ activeTags,
31
+ toggleActiveTag,
32
+ activeExperienceType,
33
+ toggleActiveExperienceType,
34
+ } = useResume(experiences)
35
+
36
+ const tabs = [
37
+ {
38
+ id: 'All',
39
+ label: translations.all,
40
+ },
41
+ {
42
+ id: 'Work',
43
+ label: translations.work,
44
+ },
45
+ {
46
+ id: 'Project',
47
+ label: translations.projects,
48
+ },
49
+ {
50
+ id: 'Education',
51
+ label: translations.education,
52
+ },
53
+ {
54
+ id: 'Other',
55
+ label: translations.other,
56
+ }
57
+ ].filter(x => x.id === 'All'
58
+ ? x
59
+ : categories.includes(x.id)
60
+ )
61
+
62
+ const sections = [
63
+ {
64
+ title: translations.work,
65
+ experiences: work
66
+ },
67
+ {
68
+ title: translations.projects,
69
+ experiences: project
70
+ },
71
+ {
72
+ title: translations.education,
73
+ experiences: education
74
+ },
75
+ {
76
+ title: translations.other,
77
+ experiences: other
78
+ }
79
+ ]
80
+
81
+ const print = () => {
82
+ window.print()
83
+ }
84
+
85
+ return (
86
+ <div data-component="@design-system-extras/resume" data-status={status}>
87
+
88
+ <style href="@design-system-extras/resume" precedence="high">{`
89
+ [data-component="@design-system-extras/resume"] {
90
+
91
+ --padding: var(--resume-padding, 0);
92
+ --background: var(--resume-background, transparent);
93
+ --header-padding: var(--resume-header-padding, 8px 24px 8px 0);
94
+ --header-justify-content: var(--resume-header-justify-content, center);
95
+ --filter-padding: var(--resume-filter-padding, var(--space-s) var(--space-m));
96
+ --content-background: var(--resume-content-background, transparent);
97
+
98
+ background: var(--background);
99
+ padding: var(--padding);
100
+ display: grid;
101
+ grid-template-columns: 1fr;
102
+ grid-template-rows: fit-content fit-content fit-content;
103
+ grid-template-areas:
104
+ "profile-summary"
105
+ "actions"
106
+ "content";
107
+ }
108
+
109
+ [data-component="@design-system-extras/resume"] [data-slot="actions"] {
110
+ min-width: 0;
111
+ box-sizing: border-box;
112
+ grid-area: actions;
113
+ display: flex;
114
+ justify-content: center;
115
+ align-items: center;
116
+ padding: var(--header-padding);
117
+ }
118
+
119
+ [data-component="@design-system-extras/resume"] [data-slot="actions"]::-webkit-scrollbar {
120
+ display: none;
121
+ }
122
+
123
+ [data-component="@design-system-extras/resume"] .secondary-actions {
124
+ display: none;
125
+ gap: var(--space-s);
126
+ }
127
+
128
+ [data-component="@design-system-extras/resume"] [data-slot="profile-summary"] {
129
+ grid-area: profile-summary;
130
+ z-index: 1;
131
+ }
132
+
133
+ [data-component="@design-system-extras/resume"] [data-slot="content"] {
134
+ display: flex;
135
+ width: 100%;
136
+ flex-direction: column;
137
+ align-items: center;
138
+ justify-content: flex-start;
139
+ grid-area: content;
140
+ padding: var(--space-l) var(--space-m);
141
+ margin: var(--content-margin);
142
+ gap: var(--space-xl);
143
+ background: var(--content-background);
144
+ }
145
+
146
+ [data-component="@design-system-extras/resume"] [data-animation="fade-in"] {
147
+ opacity: 0;
148
+ transition-property: opacity, transform;
149
+ transition-duration: 1s, 1s;
150
+ transition-timing-function: ease-in-out;
151
+ transform: translateZ(0);
152
+ }
153
+
154
+ [data-component="@design-system-extras/resume"] [data-scroll] {
155
+ scrollbar-width: none;
156
+ overflow-y: auto;
157
+ }
158
+
159
+ [data-component="@design-system-extras/resume"] [data-scroll]::-webkit-scrollbar {
160
+ display: none;
161
+ }
162
+
163
+ [data-component="@design-system-extras/resume"][data-status="Success"] [data-animation="fade-in"] {
164
+ opacity: 1;
165
+ }
166
+
167
+ [data-component="@design-system-extras/resume"] .content-section {
168
+ display: flex;
169
+ flex-direction: column;
170
+ gap: var(--space-l);
171
+ width: 100%;
172
+ max-width: 700px;
173
+ }
174
+
175
+ @media (min-width: 1200px) {
176
+ [data-component="@design-system-extras/resume"] {
177
+ height: 100vh;
178
+ grid-template-columns: 400px 1fr;
179
+ grid-template-rows: min-content 1fr;
180
+ grid-template-areas:
181
+ "profile-summary actions"
182
+ "profile-summary content";
183
+ }
184
+
185
+ [data-component="@design-system-extras/resume"] [data-slot="actions"] {
186
+ justify-content: space-between;
187
+ }
188
+
189
+ [data-component="@design-system-extras/resume"] .secondary-actions {
190
+ display: flex;
191
+ }
192
+ }
193
+
194
+ @media (min-width: 1900px) {
195
+ [data-component="@design-system-extras/resume"] [data-slot="content"] {
196
+ grid-column-start: profile-summary;
197
+ grid-column-end: content;
198
+ grid-row-start: content;
199
+ grid-row-end: content;
200
+ }
201
+ }
202
+ `}</style>
203
+
204
+ <ProfileCard
205
+ data-scroll
206
+ data-slot="profile-summary"
207
+ activeTags={activeTags}
208
+ onTagClick={toggleActiveTag}
209
+ translations={translations}
210
+ surface="primary"
211
+ {...profileCardProps}
212
+ {...profile}
213
+ />
214
+
215
+ <header className="actions" data-slot="actions" data-surface="primary">
216
+
217
+ <Tabs
218
+ tabs={tabs}
219
+ activeTabId={activeExperienceType || 'All'}
220
+ onTabClick={(_, tab) => toggleActiveExperienceType(tab.id)}
221
+ style={{
222
+ borderBottom: !!tags.length ? 'var(--header-border-bottom)' : undefined
223
+ }}
224
+ />
225
+
226
+ <div className="secondary-actions">
227
+ {
228
+ actions.map(({ label, contextMenu, ...buttonProps }) => (
229
+ !contextMenu
230
+ ? (
231
+ <Button {...buttonProps}>
232
+ {label}
233
+ </Button>
234
+ ) : (
235
+ <Dropdown trigger={(
236
+ <Button {...buttonProps}>
237
+ {label}
238
+ </Button>
239
+ )}>
240
+ <ContextMenu>
241
+ { contextMenu.map(menuItem => (
242
+ <ContextMenu.Item {...menuItem}>
243
+ {menuItem.label}
244
+ </ContextMenu.Item>
245
+ ))}
246
+ </ContextMenu>
247
+
248
+ </Dropdown>
249
+ )
250
+
251
+ ))
252
+ }
253
+ </div>
254
+
255
+
256
+ {
257
+ !!tags.length && (
258
+ <Tags
259
+ tags={tags}
260
+ activeTags={activeTags}
261
+ onSelect={toggleActiveTag}
262
+ style={{
263
+ padding: 'var(--filter-padding)',
264
+ display: 'flex',
265
+ // overflowY: 'auto'
266
+ }}
267
+ />
268
+ )
269
+ }
270
+ </header>
271
+
272
+
273
+ <main className="content" data-slot="content" data-scroll>
274
+
275
+ {
276
+ sections
277
+ .filter(x => !!x.experiences.length)
278
+ .map(({ title, experiences }) => (
279
+ <section className="content-section" key={title}>
280
+ <Title variant="primary" style={{ textAlign: 'center' }}>{title}</Title>
281
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-l)'}}>
282
+ {
283
+ experiences.map(experience =>
284
+ <ResumeExperience
285
+ key={experience.id || experience.title}
286
+ title={experience.title}
287
+ subTitle={experience.subTitle}
288
+ date={experience.date}
289
+ description={experience.description}
290
+ website={experience.website}
291
+ github={experience.github}
292
+ tags={experience.tags}
293
+ />
294
+ )
295
+ }
296
+ </div>
297
+ </section>
298
+ ))
299
+ }
300
+
301
+ </main>
302
+
303
+ </div>
304
+ )
305
+ }
@@ -0,0 +1,237 @@
1
+ import React from 'react'
2
+ import { Resume } from './Resume.jsx'
3
+
4
+ export default {
5
+ title: 'Design System Extras/Templates/Resume',
6
+ component: Resume,
7
+ parameters: {
8
+ layout: 'fullscreen',
9
+ },
10
+ }
11
+
12
+ const Story = props => {
13
+
14
+ return (
15
+ <Resume {...props}/>
16
+ )
17
+ }
18
+
19
+ export const Default = Story.bind({})
20
+ Default.args = {
21
+ status: 'Success',
22
+ actions: [
23
+ {
24
+ variant: 'neutral',
25
+ label: 'Theme: light',
26
+ contextMenu: [
27
+ { label: 'EN', onClick: () => console.log('EN') },
28
+ { label: 'SV', onClick: () => console.log('SV') },
29
+ ]
30
+ },
31
+ {
32
+ variant: 'neutral',
33
+ label: 'EN',
34
+ contextMenu: [
35
+ { label: 'EN', onClick: () => console.log('EN') },
36
+ { label: 'SV', onClick: () => console.log('SV') },
37
+ ]
38
+ },
39
+ {
40
+ variant: 'cta',
41
+ label: 'View in Google Drive',
42
+ href: '',
43
+ target: '_blank'
44
+ }
45
+ ],
46
+ translations: {
47
+ tags: 'Experience with',
48
+ work: 'Work',
49
+ projects: 'Projects',
50
+ education: 'Education',
51
+ other: 'Other',
52
+ download: 'Download',
53
+ all: 'All',
54
+ },
55
+ profile: {
56
+ name: 'John Doe',
57
+ role: 'Software Engineer',
58
+ image: 'https://d1yuixo7x29bj4.cloudfront.net/36zDqF0TKZZ5KkJdyg7NH/rJ2RHNDMS1tSw6-Akz1D8.jpeg',
59
+ summary: `
60
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
61
+ Sed id arcu arcu. Suspendisse orci sapien, accumsan eu turpis a,
62
+ tristique pretium est. Cras tempor sagittis iaculis.
63
+ Vestibulum congue ullamcorper odio vel varius.
64
+ Etiam ornare volutpat leo quis pretium.
65
+ Vestibulum eu tincidunt est.
66
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
67
+ congue lectus sit amet, condimentum sem.
68
+
69
+ Mauris vitae dui massa. In sed dignissim elit. Nunc viverra tempus ante,
70
+ facilisis aliquet augue sagittis vel.
71
+ Morbi convallis dolor nec urna mollis, et auctor nisl bibendum.
72
+ Vivamus eget ornare justo. Morbi ut malesuada augue,
73
+ vitae luctus felis. Pellentesque egestas quam ut ante mattis,
74
+ ut viverra dolor sodales.
75
+ `,
76
+ tags: ['JavaScript', 'TypeScript', 'React', 'Angular'],
77
+ links: [
78
+ {
79
+ icon: 'Call',
80
+ label: '+46 072 11 11'
81
+ },
82
+ {
83
+ icon: 'Email',
84
+ label: 'john.doe@example.com'
85
+ },
86
+ {
87
+ icon: 'Website',
88
+ label: 'https://github.com'
89
+ },
90
+ ]
91
+ },
92
+ experiences: [
93
+ {
94
+ title: 'Small undertaking',
95
+ 'Sub Title': 'Software Engineer',
96
+ date: 'Jan 2017',
97
+ description: `
98
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
99
+ Sed id arcu arcu. Suspendisse orci sapien, accumsan eu turpis a,
100
+ tristique pretium est. Cras tempor sagittis iaculis.
101
+ Vestibulum congue ullamcorper odio vel varius.
102
+ Etiam ornare volutpat leo quis pretium.
103
+ Vestibulum eu tincidunt est.
104
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
105
+ congue lectus sit amet, condimentum sem.
106
+ `,
107
+ typeOfExperience: 'Work',
108
+ tags: ['JavaScript', 'Angular']
109
+ },
110
+ {
111
+ title: 'Foo Company',
112
+ 'Sub Title': 'Software Engineer',
113
+ date: '2022 - 2023',
114
+ description: `
115
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
116
+ Sed id arcu arcu. Suspendisse orci sapien, accumsan eu turpis a,
117
+ tristique pretium est. Cras tempor sagittis iaculis.
118
+ Vestibulum congue ullamcorper odio vel varius.
119
+ Etiam ornare volutpat leo quis pretium.
120
+ Vestibulum eu tincidunt est.
121
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
122
+ congue lectus sit amet, condimentum sem.
123
+ `,
124
+ typeOfExperience: 'Work',
125
+ tags: ['JavaScript', 'Angular']
126
+ },
127
+ {
128
+ title: 'Bar Company',
129
+ 'Sub Title': 'Software Engineer',
130
+ date: '2018 - 2019',
131
+ description: `
132
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
133
+ Sed id arcu arcu. Suspendisse orci sapien, accumsan eu turpis a,
134
+ tristique pretium est. Cras tempor sagittis iaculis.
135
+ Vestibulum congue ullamcorper odio vel varius.
136
+ Etiam ornare volutpat leo quis pretium.
137
+ Vestibulum eu tincidunt est.
138
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
139
+ congue lectus sit amet, condimentum sem.
140
+ `,
141
+ typeOfExperience: 'Work',
142
+ tags: ['JavaScript', 'React']
143
+ },
144
+ {
145
+ title: 'Baz Company',
146
+ 'Sub Title': 'Software Engineer',
147
+ date: '2019 - 2021',
148
+ description: `
149
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
150
+ Sed id arcu arcu. Suspendisse orci sapien, accumsan eu turpis a,
151
+ tristique pretium est. Cras tempor sagittis iaculis.
152
+ Vestibulum congue ullamcorper odio vel varius.
153
+ Etiam ornare volutpat leo quis pretium.
154
+ Vestibulum eu tincidunt est.
155
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
156
+ congue lectus sit amet, condimentum sem.
157
+ `,
158
+ typeOfExperience: 'Work',
159
+ tags: ['TypeScript', 'Angular']
160
+ },
161
+ {
162
+ title: 'Project Baz',
163
+ 'Sub Title': 'Fun little side project',
164
+ date: 'Apr 2019',
165
+ description: `
166
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
167
+ Sed id arcu arcu. Suspendisse orci sapien, accumsan eu turpis a,
168
+ tristique pretium est. Cras tempor sagittis iaculis.
169
+ `,
170
+ typeOfExperience: 'Project',
171
+ tags: ['TypeScript', 'Angular'],
172
+ gitHub: "https://github.com",
173
+ website: "https://ossy.se"
174
+ },
175
+ {
176
+ title: 'Project with a longer name',
177
+ 'Sub Title': 'Way too big of an undertaking',
178
+ date: 'Jan 2020',
179
+ description: `
180
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
181
+ Sed id arcu arcu. Suspendisse orci sapien, accumsan eu turpis a,
182
+ tristique pretium est. Cras tempor sagittis iaculis.
183
+ Vestibulum congue ullamcorper odio vel varius.
184
+ Etiam ornare volutpat leo quis pretium.
185
+ Vestibulum eu tincidunt est.
186
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
187
+ congue lectus sit amet, condimentum sem.
188
+ `,
189
+ typeOfExperience: 'Project',
190
+ tags: ['TypeScript', 'Angular'],
191
+ gitHub: "https://github.com",
192
+ website: "https://ossy.se"
193
+ },
194
+ {
195
+ title: 'Upper secondary school',
196
+ 'Sub Title': 'It program',
197
+ date: '2016 - 2018',
198
+ description: `
199
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
200
+ congue lectus sit amet, condimentum sem.
201
+ `,
202
+ typeOfExperience: 'Education',
203
+ tags: ['TypeScript', 'JavaScript', 'React']
204
+ },
205
+ {
206
+ title: 'A good course',
207
+ 'Sub Title': 'I learned a lot',
208
+ date: 'Feb 2021',
209
+ description: `
210
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
211
+ congue lectus sit amet, condimentum sem.
212
+ `,
213
+ typeOfExperience: 'Education',
214
+ tags: ['TypeScript', 'JavaScript', 'React']
215
+ },
216
+ {
217
+ title: 'School',
218
+ 'Sub Title': 'It was boring',
219
+ date: '2000 - 2008',
220
+ description: `
221
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
222
+ congue lectus sit amet, condimentum sem.
223
+ `,
224
+ typeOfExperience: 'Education'
225
+ },
226
+ {
227
+ title: 'I like rocks',
228
+ 'Sub Title': 'Wanna see my rock collection?',
229
+ date: '2024',
230
+ description: `
231
+ Nunc id quam at lectus condimentum laoreet ac nec nibh. Aenean a tortor hendrerit,
232
+ congue lectus sit amet, condimentum sem.
233
+ `,
234
+ typeOfExperience: 'Other'
235
+ }
236
+ ]
237
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Resume.jsx'
2
+ export * from './useResume.js'
@@ -0,0 +1,85 @@
1
+ import React, { useState, useEffect } from 'react'
2
+
3
+ const categorizeExperiences = experiences => experiences.reduce((categories, exp) => {
4
+ categories[exp.typeOfExperience || 'Other'].push(exp)
5
+ return categories
6
+ }, { Work: [], Education: [], Project: [], Other: [] })
7
+
8
+ const experienceByDate = (a, b) => {
9
+ const getDate = x => x?.date?.split(' - ')?.[0] || '1900'
10
+ const dateA = new Date(getDate(a))
11
+ const dateB = new Date(getDate(b))
12
+ return dateB - dateA
13
+ }
14
+
15
+ export const useResume = (experiences = []) => {
16
+ // Haram to do it in place I know,
17
+ // but feels unneccecary to do it each time we filter them
18
+ experiences.sort(experienceByDate)
19
+
20
+ const [work, setWork] = useState([])
21
+ const [education, setEducation] = useState([])
22
+ const [project, setProject] = useState([])
23
+ const [other, setOther] = useState([])
24
+ const [activeTags, setActiveTags] = useState([])
25
+ const [activeExperienceType, setActiveExperienceType] = useState()
26
+
27
+ const tags = Object.keys(
28
+ experiences
29
+ .flatMap(x => x.tags)
30
+ .filter(x => !!x)
31
+ .reduce((acc, x) => ({ [x]: '', ...acc }), {})
32
+ )
33
+
34
+ const categories = Object.keys(
35
+ experiences // get uniquie typeOfExperience
36
+ .reduce((acc, x) => ({ [x.typeOfExperience]: '', ...acc }), {})
37
+ )
38
+
39
+ useEffect(() => {
40
+ let filteredExperiences = experiences
41
+
42
+ if (!!activeTags.length) {
43
+ filteredExperiences = experiences.filter(experience => experience?.tags
44
+ ?.map(x => x.toLowerCase())
45
+ ?.some(tag => activeTags.map(x => x.toLowerCase()).includes(tag)))
46
+ }
47
+
48
+ if (!!activeExperienceType) {
49
+ filteredExperiences = filteredExperiences
50
+ .filter(x => x.typeOfExperience === activeExperienceType)
51
+ }
52
+
53
+ const categorizedExperiences = categorizeExperiences(filteredExperiences)
54
+ setWork(categorizedExperiences.Work)
55
+ setEducation(categorizedExperiences.Education)
56
+ setProject(categorizedExperiences.Project)
57
+ setOther(categorizedExperiences.Other)
58
+
59
+ }, [activeTags, experiences, activeExperienceType])
60
+
61
+ const toggleActiveExperienceType = type => {
62
+ if (type === activeExperienceType) return
63
+ setActiveExperienceType(type === 'All' ? undefined : type)
64
+ }
65
+
66
+ const toggleActiveTag = tag => {
67
+ setActiveTags(tags => tags.includes(tag)
68
+ ? tags.filter(x => x !== tag)
69
+ : [...tags, tag]
70
+ )
71
+ }
72
+
73
+ return {
74
+ work,
75
+ education,
76
+ project,
77
+ other,
78
+ tags: [],
79
+ categories,
80
+ activeTags,
81
+ toggleActiveTag,
82
+ activeExperienceType,
83
+ toggleActiveExperienceType
84
+ }
85
+ }