@soul-refiner/design-system 0.1.26 → 0.1.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soul-refiner/design-system",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "main": "src/index.ts",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@emoji-mart/react": "^1.1.1",
50
- "@udecode/plate": "^21.1.2",
50
+ "@udecode/plate": "^22.0.1",
51
51
  "emoji-mart": "^5.5.2",
52
52
  "react": "^18.2.0",
53
53
  "react-dnd": "^16.0.1",
@@ -59,23 +59,24 @@
59
59
  "slate": "^0.94.1",
60
60
  "slate-history": "^0.93.0",
61
61
  "slate-hyperscript": "^0.77.0",
62
- "slate-react": "^0.94.0",
62
+ "slate-react": "^0.97.1",
63
63
  "styled-components": "^6.0.0-rc.3"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "@emoji-mart/react": "^1.1.1",
67
- "@udecode/plate": "^21.1.2",
67
+ "@udecode/plate": "^22.0.1",
68
68
  "emoji-mart": "^5.5.2",
69
69
  "react": "^18.2.0",
70
70
  "react-dnd": "^16.0.1",
71
71
  "react-dnd-html5-backend": "^16.0.1",
72
72
  "react-dom": "^18.2.0",
73
73
  "react-is": "^18.2.0",
74
+ "react-tooltip": "^5.18.0",
74
75
  "scheduler": "^0.23.0",
75
76
  "slate": "^0.94.1",
76
77
  "slate-history": "^0.93.0",
77
78
  "slate-hyperscript": "^0.77.0",
78
- "slate-react": "^0.94.0",
79
- "styled-components": "^6.0.0-rc.1"
79
+ "slate-react": "^0.97.1",
80
+ "styled-components": "^6.0.0-rc.3"
80
81
  }
81
82
  }
@@ -42,6 +42,6 @@ export const Typography = styled.p<TypographyProps>`
42
42
  ${Styles.color}
43
43
  ${Styles.weight}
44
44
  ${Styles.typography}
45
- ${Styles.textClamp}
46
45
  ${Styles.styleable}
46
+ ${({ $textClampLines }) => ($textClampLines ? Styles.textClamp : '')}
47
47
  `;
@@ -1,6 +1,5 @@
1
1
  export {
2
2
  ELEMENT_BLOCKQUOTE,
3
- ELEMENT_CODE_BLOCK,
4
3
  ELEMENT_CODE_LINE,
5
4
  ELEMENT_CODE_SYNTAX,
6
5
  ELEMENT_DEFAULT,
@@ -55,13 +54,13 @@ export const ELEMENT_QA = 'q-n-a';
55
54
  export const ELEMENT_RECIPE = 'recipe';
56
55
  export const ELEMENT_VIDEO = 'video';
57
56
 
58
- export const BUTTON_COMMENT = 'button-comment';
59
- export const BUTTON_GIFT_SUBSCRIPTION = 'button-gift-subscription';
57
+ // export const BUTTON_COMMENT = 'button-comment';
58
+ // export const BUTTON_GIFT_SUBSCRIPTION = 'button-gift-subscription';
59
+ // export const BUTTON_SHARE = 'button-share';
60
+ // export const BUTTON_SHARE_PAGE = 'button-share-page';
61
+ // export const BUTTON_SIGNUP = 'signup';
62
+ // export const BUTTON_SPECIAL_OFFER = 'button-special-offer';
60
63
  export const BUTTON_LINK = 'button-link';
61
- export const BUTTON_SHARE = 'button-share';
62
- export const BUTTON_SHARE_PAGE = 'button-share-page';
63
- export const BUTTON_SIGNUP = 'signup';
64
- export const BUTTON_SPECIAL_OFFER = 'button-special-offer';
65
64
  export const BUTTON_SUBSCRIBE = 'button-subscribe';
66
65
 
67
66
  export const COLORS = [
@@ -86,103 +85,48 @@ export const DEFAULT_CONTENT = [
86
85
  },
87
86
  ],
88
87
  },
89
- // {
90
- // id: "video-234jlnlknsdlkfjwlkjaa",
91
- // parentId: "course-1lk4j3k4jlkj2l4k2j34lkj",
92
- // children: [
93
- // {
94
- // text: "The DNA of a Warrior",
95
- // },
96
- // ],
97
- // captions: [],
98
- // duration: 3765,
99
- // source: "",
100
- // synopsis:
101
- // "Understanding the battlefield is the first key to winning the war for sexual purity. In this episode, you will discover hell's strategies against the 21st Century Church and be introduced to the four components of the noose of sexual bondage. Learn the first key steps to freedom and how to stay pure.",
102
- // thumbnail: "video-thumbnail.jpg",
103
- // title: "The DNA of a Warrior",
104
- // type: "video",
105
- // },
106
- // {
107
- // type: "p",
108
- // children: [
109
- // {
110
- // text: "Welcome to Warpath. You’re about to embark on a journey towards lifelong healing, freedom and victory. This is a season of training. It’s sanctification on steroids, not for the faint-hearted, but for those who truly desire to live the rest of their lives for Jesus. No reserves, no retreats, no regrets.",
111
- // },
112
- // {
113
- // type: "footnote",
114
- // children: [
115
- // {
116
- // text: "This is a footnote",
117
- // },
118
- // ],
119
- // },
120
- // {
121
- // text: " What was once your deepest, darkest secret will now become the word of your testimony that heals and liberates others.",
122
- // },
123
- // ],
124
- // },
125
- // {
126
- // type: "q-n-a",
127
- // children: [
128
- // {
129
- // type: "p",
130
- // children: [
131
- // {
132
- // text: "Ask your question...",
133
- // },
134
- // ],
135
- // },
136
- // ],
137
- // },
138
88
  {
139
89
  type: 'p',
140
90
  children: [
141
91
  {
142
- text: 'In this new season, God is calling you up. He’s calling you to a higher level, a place where you’re no longer part of the problem but are becoming a part of the solution.',
92
+ text: 'Welcome to Warpath. You’re about to embark on a journey towards lifelong healing, freedom and victory. This is a season of training. It’s sanctification on steroids, not for the faint-hearted, but for those who truly desire to live the rest of their lives for Jesus. No reserves, no retreats, no regrets.',
143
93
  },
144
- // {
145
- // type: "footnote",
146
- // children: [
147
- // {
148
- // text: "This is another footnote",
149
- // },
150
- // ],
151
- // },
152
94
  {
153
- text: '',
95
+ text: ' What was once your deepest, darkest secret will now become the word of your testimony that heals and liberates others.',
154
96
  },
155
97
  ],
156
98
  },
157
99
  {
158
- type: 'blockquote',
100
+ type: 'p',
159
101
  children: [
160
102
  {
161
- text: '"Warpath isn’t about just winning for you; that’s the beginning. Warpath is about you taking the battle and winning for others so they can have their families, and their marriages, and their destinies." \n— ',
103
+ text: 'In this new season, God is calling you up. He’s calling you to a higher level, a place where you’re no longer part of the problem but are becoming a part of the solution.',
162
104
  },
163
105
  {
164
- text: 'Dr. Doug Weiss',
165
- italic: true,
106
+ text: '',
166
107
  },
167
108
  ],
168
109
  },
169
110
  {
170
- type: 'hr',
111
+ type: 'blockquote',
171
112
  children: [
172
113
  {
173
- text: '',
114
+ text: '"Warpath isn’t about just winning for you; that’s the beginning. Warpath is about you taking the battle and winning for others so they can have their families, and their marriages, and their destinies." \n— ',
174
115
  },
175
- ],
176
- },
177
- {
178
- type: 'h2',
179
- children: [
180
116
  {
181
- text: 'Lesson Overview',
182
- bold: true,
117
+ text: 'Dr. Doug Weiss',
118
+ italic: true,
183
119
  },
184
120
  ],
185
121
  },
122
+ // {
123
+ // type: 'hr',
124
+ // children: [
125
+ // {
126
+ // text: '',
127
+ // },
128
+ // ],
129
+ // },
186
130
  {
187
131
  type: 'h3',
188
132
  align: 'center',
@@ -193,14 +137,14 @@ export const DEFAULT_CONTENT = [
193
137
  },
194
138
  ],
195
139
  },
196
- {
197
- type: 'hr',
198
- children: [
199
- {
200
- text: '',
201
- },
202
- ],
203
- },
140
+ // {
141
+ // type: 'hr',
142
+ // children: [
143
+ // {
144
+ // text: '',
145
+ // },
146
+ // ],
147
+ // },
204
148
  {
205
149
  type: 'img',
206
150
  url: 'https://source.unsplash.com/1600x900/?christian',
@@ -226,57 +170,6 @@ export const DEFAULT_CONTENT = [
226
170
  },
227
171
  ],
228
172
  },
229
- {
230
- type: 'code_block',
231
- lang: 'typescript',
232
- children: [
233
- {
234
- type: 'code_line',
235
- children: [
236
- {
237
- text: 'const variable = "DO_NOT_USE"',
238
- },
239
- ],
240
- },
241
- ],
242
- },
243
- // {
244
- // type: "paywall",
245
- // children: [
246
- // {
247
- // type: "h2",
248
- // children: [
249
- // {
250
- // text: "Keep reading with a paid subscription",
251
- // },
252
- // ],
253
- // },
254
- // {
255
- // type: "p",
256
- // children: [
257
- // {
258
- // text: "Subscribe to ",
259
- // },
260
- // {
261
- // text: "Soul Refiner",
262
- // italic: true,
263
- // },
264
- // {
265
- // text: " to keep reading this content.",
266
- // },
267
- // ],
268
- // },
269
- // {
270
- // type: "button",
271
- // buttonType: "button-subscribe",
272
- // children: [
273
- // {
274
- // text: "Get Access",
275
- // },
276
- // ],
277
- // },
278
- // ],
279
- // },
280
173
  {
281
174
  type: 'p',
282
175
  children: [
@@ -13,10 +13,15 @@ const plugins = createPlugins([...blocks, ...inline, ...marks, ...utils], {
13
13
  export interface EditorProps {
14
14
  communityName: string;
15
15
  editable: boolean;
16
+ handleUploadImage: (dataURL: File) => Promise<string>;
16
17
  onChange: (newNodes: TElement[]) => void;
17
18
  }
18
19
 
19
- export const Editor = ({ communityName, editable, onChange }: EditorProps) => {
20
+ export const Editor = ({
21
+ editable,
22
+ handleUploadImage,
23
+ onChange,
24
+ }: EditorProps) => {
20
25
  return (
21
26
  <PlateProvider
22
27
  id="main"
@@ -25,7 +30,7 @@ export const Editor = ({ communityName, editable, onChange }: EditorProps) => {
25
30
  plugins={plugins}
26
31
  >
27
32
  <Wrapper>
28
- <EditorBar communityName={communityName} />
33
+ <EditorBar handleUploadImage={handleUploadImage} />
29
34
  <Plate
30
35
  id="main"
31
36
  editableProps={{
@@ -43,6 +48,44 @@ export const Editor = ({ communityName, editable, onChange }: EditorProps) => {
43
48
  const Wrapper = styled.div`
44
49
  * {
45
50
  font-family: Inter;
51
+ outline: none;
52
+ }
53
+
54
+ .slate-bold {
55
+ font-weight: 700;
56
+ }
57
+
58
+ .slate-italic {
59
+ font-style: italic;
60
+ }
61
+
62
+ .slate-strikethrough {
63
+ text-decoration: line-through;
64
+ }
65
+
66
+ .slate-underline {
67
+ text-decoration: underline;
68
+ }
69
+
70
+ li {
71
+ color: var(--color-grey-3);
72
+ }
73
+
74
+ .slate-code {
75
+ & > * {
76
+ font-family: 'Roboto Mono';
77
+ & > * {
78
+ font-family: 'Roboto Mono';
79
+ & > * {
80
+ font-family: 'Roboto Mono';
81
+ }
82
+ }
83
+ }
84
+
85
+ background-color: var(--color-grey-9);
86
+ padding: 0.4em 0.4em;
87
+ font-size: 0.8em;
88
+ line-height: 0.8em;
46
89
  }
47
90
 
48
91
  & > [data-slate-editor='true'] > * {
@@ -1,12 +1,14 @@
1
1
  import React from 'react';
2
2
  import {
3
3
  Alignment,
4
- AlignToolbarButton,
5
- isCollapsed,
4
+ // isCollapsed,
6
5
  PlateEditor,
7
6
  setAlign,
8
- someNode,
9
7
  } from '@udecode/plate';
8
+ import {
9
+ useAlignDropdownMenu,
10
+ useAlignDropdownMenuState,
11
+ } from '@udecode/plate-alignment';
10
12
  import { Dropdown } from 'components';
11
13
  import {
12
14
  PlayerListPlay,
@@ -15,12 +17,41 @@ import {
15
17
  AlignLeft,
16
18
  AlignRight,
17
19
  } from 'icons';
20
+ import { MenuButton } from './navbar';
18
21
 
19
22
  export interface AlignmentDropdownProps {
20
23
  editorRef: PlateEditor;
21
24
  }
22
25
 
26
+ const items = [
27
+ {
28
+ value: 'left',
29
+ icon: AlignLeft,
30
+ },
31
+ {
32
+ value: 'center',
33
+ icon: AlignCenter,
34
+ },
35
+ {
36
+ value: 'right',
37
+ icon: AlignRight,
38
+ },
39
+ {
40
+ value: 'justify',
41
+ icon: AlignJustified,
42
+ },
43
+ ];
44
+
23
45
  export const AlignmentDropdown = ({ editorRef }: AlignmentDropdownProps) => {
46
+ const alignState = useAlignDropdownMenuState();
47
+ const { radioGroupProps } = useAlignDropdownMenu(alignState);
48
+
49
+ const current = radioGroupProps.value;
50
+
51
+ const IconValue =
52
+ items.find((item) => item.value === radioGroupProps.value)?.icon ??
53
+ PlayerListPlay;
54
+
24
55
  const handleAlign = (event: React.FormEvent) => {
25
56
  const value = (event.target as HTMLElement)?.getAttribute(
26
57
  'data-value'
@@ -33,62 +64,79 @@ export const AlignmentDropdown = ({ editorRef }: AlignmentDropdownProps) => {
33
64
  });
34
65
  };
35
66
 
36
- const alignment = (() => {
37
- if (!isCollapsed(editorRef?.selection)) {
38
- return 'multiple-lines';
39
- } else if (
40
- someNode(editorRef, { match: { align: 'center' } }) ? true : undefined
41
- ) {
42
- return 'center';
43
- } else if (
44
- someNode(editorRef, { match: { align: 'right' } }) ? true : undefined
45
- ) {
46
- return 'right';
47
- } else if (
48
- someNode(editorRef, { match: { align: 'justify' } }) ? true : undefined
49
- ) {
50
- return 'justify';
51
- } else {
52
- return 'left';
53
- }
54
- })();
55
-
56
67
  return (
57
68
  <Dropdown
58
- closeOnBlur={false}
59
- closeOnChange={false}
60
69
  onChange={handleAlign}
70
+ offset={[0, 0]}
71
+ options={[
72
+ {
73
+ icon: (
74
+ <MenuButton $active={current === 'left'}>
75
+ <AlignLeft size={24} color="grey-5" />
76
+ </MenuButton>
77
+ ),
78
+ sx: 'gap: 0px; padding: 4px;',
79
+ value: 'left',
80
+ },
81
+ {
82
+ icon: (
83
+ <MenuButton $active={current === 'center'}>
84
+ <AlignCenter size={24} color="grey-5" />
85
+ </MenuButton>
86
+ ),
87
+ sx: 'gap: 0px; padding: 4px;',
88
+ value: 'center',
89
+ },
90
+ {
91
+ icon: (
92
+ <MenuButton $active={current === 'right'}>
93
+ <AlignRight size={24} color="grey-5" />
94
+ </MenuButton>
95
+ ),
96
+ sx: 'gap: 0px; padding: 4px;',
97
+ value: 'right',
98
+ },
99
+ {
100
+ icon: (
101
+ <MenuButton $active={current === 'justify'}>
102
+ <AlignJustified size={24} color="grey-5" />
103
+ </MenuButton>
104
+ ),
105
+ sx: 'gap: 0px; padding: 4px;',
106
+ value: 'justify',
107
+ },
108
+ ]}
61
109
  maintainState={false}
62
- trigger={<PlayerListPlay size={24} color="grey-5" />}
63
- >
64
- <AlignToolbarButton
65
- as="div"
66
- active={alignment === 'left' ? true : undefined}
67
- data-value="left"
68
- value="left"
69
- icon={<AlignLeft size={24} color="grey-5" />}
70
- />
71
- <AlignToolbarButton
72
- as="div"
73
- active={alignment === 'center' ? true : undefined}
74
- data-value="center"
75
- value="center"
76
- icon={<AlignCenter size={24} color="grey-5" />}
77
- />
78
- <AlignToolbarButton
79
- as="div"
80
- active={alignment === 'right' ? true : undefined}
81
- data-value="right"
82
- value="right"
83
- icon={<AlignRight size={24} color="grey-5" />}
84
- />
85
- <AlignToolbarButton
86
- as="div"
87
- active={alignment === 'justify' ? true : undefined}
88
- data-value="justify"
89
- value="justify"
90
- icon={<AlignJustified size={24} color="grey-5" />}
91
- />
92
- </Dropdown>
110
+ trigger={<IconValue size={24} color="grey-5" />}
111
+ />
93
112
  );
94
113
  };
114
+
115
+ // <AlignToolbarButton
116
+ // as="div"
117
+ // active={alignment === 'left' ? true : undefined}
118
+ // data-value="left"
119
+ // value="left"
120
+ // icon={<AlignLeft size={24} color="grey-5" />}
121
+ // />
122
+ // <AlignToolbarButton
123
+ // as="div"
124
+ // active={alignment === 'center' ? true : undefined}
125
+ // data-value="center"
126
+ // value="center"
127
+ // icon={<AlignCenter size={24} color="grey-5" />}
128
+ // />
129
+ // <AlignToolbarButton
130
+ // as="div"
131
+ // active={alignment === 'right' ? true : undefined}
132
+ // data-value="right"
133
+ // value="right"
134
+ // icon={<AlignRight size={24} color="grey-5" />}
135
+ // />
136
+ // <AlignToolbarButton
137
+ // as="div"
138
+ // active={alignment === 'justify' ? true : undefined}
139
+ // data-value="justify"
140
+ // value="justify"
141
+ // icon={<AlignJustified size={24} color="grey-5" />}
142
+ // />
@@ -0,0 +1,146 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { Box, Button, H, Modal, P, Radio } from 'components';
4
+ import { Close, InsertButton } from 'icons';
5
+ import { MenuButton } from './navbar';
6
+ import { BUTTON_LINK, BUTTON_SUBSCRIBE } from '../constants';
7
+
8
+ interface ButtonModalProps {
9
+ onButtonInsert: (type: string) => void;
10
+ }
11
+
12
+ export const ButtonModal = ({ onButtonInsert }: ButtonModalProps) => {
13
+ const [buttonType, setButtonType] = React.useState<string | undefined>();
14
+
15
+ const handleButtonInsert = () => {
16
+ buttonType && onButtonInsert(buttonType);
17
+ setButtonType(undefined);
18
+ };
19
+
20
+ return (
21
+ <Modal>
22
+ <Modal.Trigger>
23
+ <MenuButton>
24
+ <InsertButton size={24} color="grey-5" />
25
+ </MenuButton>
26
+ </Modal.Trigger>
27
+ <Modal.Content sx="border-radius: 16px; max-width: 782px; width: 100%; margin: 24px; padding: 0px;">
28
+ <Box $dir="column">
29
+ <Box
30
+ $align="center"
31
+ $justify="space-between"
32
+ sx="padding: 24px; box-shadow: var(--elevation-2);"
33
+ >
34
+ <Box $align="center" $gap={32}>
35
+ <IconWrapper>
36
+ <InsertButton size={24} color="hue-blue-1" />
37
+ </IconWrapper>
38
+ <H $bold $color="hue-blue-1" $fontSize={20} $lineHeight={28}>
39
+ Insert Button
40
+ </H>
41
+ </Box>
42
+ <Close
43
+ size={24}
44
+ sx="&:hover { cursor: pointer; border-radius: 50%; background-color: var(--color-grey-9); }"
45
+ />
46
+ </Box>
47
+ <Box $dir="column" $gap={24} sx="padding: 24px 48px 24px 24px;">
48
+ <ButtonItem
49
+ active={buttonType === BUTTON_SUBSCRIBE}
50
+ description="A button encouraging the reader to purchase a paid subscription to your community"
51
+ onClick={() => setButtonType(BUTTON_SUBSCRIBE)}
52
+ title="Subscribe Now"
53
+ >
54
+ <Button size="small" sx="width: 130px;">
55
+ Subscribe
56
+ </Button>
57
+ </ButtonItem>
58
+ <ButtonItem
59
+ active={buttonType === BUTTON_LINK}
60
+ description="A button that links to a URL"
61
+ onClick={() => setButtonType(BUTTON_LINK)}
62
+ title="Link Button"
63
+ >
64
+ <Button size="small" sx="width: 130px;">
65
+ Link
66
+ </Button>
67
+ </ButtonItem>
68
+ </Box>
69
+ <Box
70
+ $padding={24}
71
+ $gap={16}
72
+ sx="padding: 24px; background-color: var(--color-white); border-top: 1px solid var(--color-grey-8);"
73
+ >
74
+ <Modal.Dismiss>
75
+ <Button
76
+ size="small"
77
+ sx="flex: 1;"
78
+ type="outlined"
79
+ onClick={() => setButtonType(undefined)}
80
+ >
81
+ Cancel
82
+ </Button>
83
+ </Modal.Dismiss>
84
+ <Modal.Dismiss>
85
+ <Button size="small" sx="flex: 1;" onClick={handleButtonInsert}>
86
+ Confirm
87
+ </Button>
88
+ </Modal.Dismiss>
89
+ </Box>
90
+ </Box>
91
+ </Modal.Content>
92
+ </Modal>
93
+ );
94
+ };
95
+
96
+ const IconWrapper = styled.div`
97
+ height: 48px;
98
+ width: 48px;
99
+ box-sizing: border-box;
100
+ padding: 12px;
101
+ border-radius: 8px;
102
+ background-color: var(--color-hue-blue-2);
103
+ `;
104
+
105
+ interface ButtonItemProps extends React.PropsWithChildren {
106
+ active?: boolean;
107
+ description: string;
108
+ onClick: () => void;
109
+ title: string;
110
+ }
111
+
112
+ const ButtonItem = ({
113
+ active,
114
+ children,
115
+ description,
116
+ onClick,
117
+ title,
118
+ }: ButtonItemProps) => (
119
+ <Box
120
+ $dir="column"
121
+ onClick={onClick}
122
+ sx="border-radius: 16px; box-shadow: var(--elevation-2); cursor: pointer;"
123
+ >
124
+ <Box
125
+ sx={`
126
+ padding: 24px;
127
+ background-color: var(--color-blue-10);
128
+ border-radius: 8px 8px 0px 0px;
129
+ border-bottom: 1px solid var(--color-grey-9);
130
+ `}
131
+ >
132
+ <Radio checked={!!active} sx="align-items: flex-start;" value={title} />
133
+ <Box $dir="column">
134
+ <P $semiBold>{title}</P>
135
+ <P>{description}</P>
136
+ </Box>
137
+ </Box>
138
+ <Box
139
+ $align="center"
140
+ $justify="center"
141
+ sx="border-radius: 0px 0px 8px 8px; padding: 24px;"
142
+ >
143
+ {children}
144
+ </Box>
145
+ </Box>
146
+ );