@licklist/design 0.78.28 → 0.78.30

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 (144) hide show
  1. package/dist/assets/Trend-Down.svg.js +16 -0
  2. package/dist/assets/Trend-Up.svg.js +16 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +24 -0
  6. package/dist/v2/components/Alert/Alert.js +87 -0
  7. package/dist/v2/components/Alert/Alert.scss.js +6 -0
  8. package/dist/v2/components/Button/Button.d.ts +8 -4
  9. package/dist/v2/components/Button/Button.d.ts.map +1 -1
  10. package/dist/v2/components/Button/Button.js +121 -0
  11. package/dist/v2/components/Button/Button.scss.js +6 -0
  12. package/dist/v2/components/Button/index.d.ts +2 -2
  13. package/dist/v2/components/Button/index.d.ts.map +1 -1
  14. package/dist/v2/components/Checkbox/Checkbox.d.ts +9 -0
  15. package/dist/v2/components/Checkbox/Checkbox.d.ts.map +1 -0
  16. package/dist/v2/components/Checkbox/Checkbox.js +231 -0
  17. package/dist/v2/components/Checkbox/Checkbox.scss.js +6 -0
  18. package/dist/v2/components/Checkbox/index.d.ts +3 -0
  19. package/dist/v2/components/Checkbox/index.d.ts.map +1 -0
  20. package/dist/v2/components/FormField/FormField.d.ts +10 -0
  21. package/dist/v2/components/FormField/FormField.d.ts.map +1 -0
  22. package/dist/v2/components/FormField/FormField.js +98 -0
  23. package/dist/v2/components/FormField/FormField.scss.js +6 -0
  24. package/dist/v2/components/FormField/index.d.ts +3 -0
  25. package/dist/v2/components/FormField/index.d.ts.map +1 -0
  26. package/dist/v2/components/NPSScore/NPSScore.js +546 -0
  27. package/dist/v2/components/NPSScore/NPSScore.scss.js +6 -0
  28. package/dist/v2/components/NewInput/NewInput.d.ts +20 -0
  29. package/dist/v2/components/NewInput/NewInput.d.ts.map +1 -0
  30. package/dist/v2/components/NewInput/NewInput.js +134 -0
  31. package/dist/v2/components/NewInput/index.d.ts +2 -0
  32. package/dist/v2/components/NewInput/index.d.ts.map +1 -0
  33. package/dist/v2/components/NewPageHeader/NewPageHeader.d.ts +10 -0
  34. package/dist/v2/components/NewPageHeader/NewPageHeader.d.ts.map +1 -0
  35. package/dist/v2/components/NewPageHeader/NewPageHeader.js +36 -0
  36. package/dist/v2/components/NewPageHeader/NewPageHeader.scss.js +6 -0
  37. package/dist/v2/components/NewPageHeader/index.d.ts +2 -0
  38. package/dist/v2/components/NewPageHeader/index.d.ts.map +1 -0
  39. package/dist/v2/components/SectionHeader/SectionHeader.d.ts +8 -0
  40. package/dist/v2/components/SectionHeader/SectionHeader.d.ts.map +1 -0
  41. package/dist/v2/components/SectionHeader/SectionHeader.js +13 -0
  42. package/dist/v2/components/SectionHeader/SectionHeader.scss.js +6 -0
  43. package/dist/v2/components/SectionHeader/index.d.ts +3 -0
  44. package/dist/v2/components/SectionHeader/index.d.ts.map +1 -0
  45. package/dist/v2/components/Select/Select.js +128 -0
  46. package/dist/v2/components/Select/Select.scss.js +6 -0
  47. package/dist/v2/components/WYSIWYGEditor/Icons.d.ts +16 -0
  48. package/dist/v2/components/WYSIWYGEditor/Icons.d.ts.map +1 -0
  49. package/dist/v2/components/WYSIWYGEditor/Icons.js +221 -0
  50. package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.d.ts +14 -0
  51. package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.d.ts.map +1 -0
  52. package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.js +358 -0
  53. package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.scss.js +6 -0
  54. package/dist/v2/components/WYSIWYGEditor/index.d.ts +3 -0
  55. package/dist/v2/components/WYSIWYGEditor/index.d.ts.map +1 -0
  56. package/dist/v2/components/index.d.ts +22 -0
  57. package/dist/v2/components/index.d.ts.map +1 -0
  58. package/dist/v2/dashboard-analytics/blog-posts/Blog.js +103 -0
  59. package/dist/v2/dashboard-analytics/blog-posts/Blog.scss.js +6 -0
  60. package/dist/v2/dashboard-analytics/chart/Chart.js +733 -0
  61. package/dist/v2/dashboard-analytics/chart/Chart.scss.js +6 -0
  62. package/dist/v2/dashboard-analytics/dashboard/Dashboard.js +270 -0
  63. package/dist/v2/dashboard-analytics/dashboard/Dashboard.scss.js +6 -0
  64. package/dist/v2/dashboard-analytics/metric-card/MetricCard.js +65 -0
  65. package/dist/v2/dashboard-analytics/metric-card/MetricCard.scss.js +6 -0
  66. package/dist/v2/dashboard-analytics/venue-card/VenueCard.js +50 -0
  67. package/dist/v2/dashboard-analytics/venue-card/VenueCard.scss.js +6 -0
  68. package/dist/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.js +48 -0
  69. package/dist/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.scss.js +6 -0
  70. package/dist/v2/icons/index.js +61 -1
  71. package/dist/v2/index.d.ts +3 -1
  72. package/dist/v2/index.d.ts.map +1 -1
  73. package/dist/v2/navigation/DashboardLayout/TopNavigation.scss.js +1 -1
  74. package/dist/v2/pages/Settings/SettingsPage.d.ts +13 -0
  75. package/dist/v2/pages/Settings/SettingsPage.d.ts.map +1 -0
  76. package/dist/v2/pages/Settings/SettingsPage.js +88 -0
  77. package/dist/v2/pages/Settings/SettingsPage.scss.js +6 -0
  78. package/dist/v2/pages/Settings/SettingsTabs.d.ts +14 -0
  79. package/dist/v2/pages/Settings/SettingsTabs.d.ts.map +1 -0
  80. package/dist/v2/pages/Settings/SettingsTabs.js +29 -0
  81. package/dist/v2/pages/Settings/SettingsTabs.scss.js +6 -0
  82. package/dist/v2/pages/Settings/components/SidebarCustomisation.js +283 -0
  83. package/dist/v2/pages/Settings/components/SidebarCustomisation.scss.js +6 -0
  84. package/dist/v2/pages/Settings/components/SidebarNavItem.d.ts +19 -0
  85. package/dist/v2/pages/Settings/components/SidebarNavItem.d.ts.map +1 -0
  86. package/dist/v2/pages/Settings/components/SidebarNavItem.js +41 -0
  87. package/dist/v2/pages/Settings/components/SidebarNavItem.scss.js +6 -0
  88. package/dist/v2/pages/Settings/components/index.d.ts +5 -0
  89. package/dist/v2/pages/Settings/components/index.d.ts.map +1 -0
  90. package/dist/v2/pages/Settings/index.d.ts +7 -0
  91. package/dist/v2/pages/Settings/index.d.ts.map +1 -0
  92. package/dist/v2/styles/form/NewInput.scss.js +6 -0
  93. package/package.json +3 -3
  94. package/src/index.ts +4 -1
  95. package/src/v2/components/Alert/Alert.scss +3 -3
  96. package/src/v2/components/Button/Button.tsx +34 -12
  97. package/src/v2/components/Button/index.ts +2 -2
  98. package/src/v2/components/Checkbox/Checkbox.scss +211 -0
  99. package/src/v2/components/Checkbox/Checkbox.stories.tsx +316 -0
  100. package/src/v2/components/Checkbox/Checkbox.tsx +106 -0
  101. package/src/v2/components/Checkbox/index.ts +3 -0
  102. package/src/v2/components/FormField/FormField.scss +87 -0
  103. package/src/v2/components/FormField/FormField.stories.tsx +71 -0
  104. package/src/v2/components/FormField/FormField.tsx +37 -0
  105. package/src/v2/components/FormField/index.ts +3 -0
  106. package/src/v2/components/NewInput/NewInput.stories.tsx +433 -0
  107. package/src/v2/components/NewInput/NewInput.tsx +96 -0
  108. package/src/v2/components/NewInput/index.ts +1 -0
  109. package/src/v2/components/NewPageHeader/NewPageHeader.scss +47 -0
  110. package/src/v2/components/NewPageHeader/NewPageHeader.stories.tsx +44 -0
  111. package/src/v2/components/NewPageHeader/NewPageHeader.tsx +35 -0
  112. package/src/v2/components/NewPageHeader/index.ts +1 -0
  113. package/src/v2/components/SectionHeader/SectionHeader.scss +11 -0
  114. package/src/v2/components/SectionHeader/SectionHeader.tsx +15 -0
  115. package/src/v2/components/SectionHeader/index.ts +2 -0
  116. package/src/v2/components/Select/Select.scss +5 -5
  117. package/src/v2/components/WYSIWYGEditor/Icons.tsx +93 -0
  118. package/src/v2/components/WYSIWYGEditor/WYSIWYGEditor.scss +310 -0
  119. package/src/v2/components/WYSIWYGEditor/WYSIWYGEditor.stories.tsx +252 -0
  120. package/src/v2/components/WYSIWYGEditor/WYSIWYGEditor.tsx +393 -0
  121. package/src/v2/components/WYSIWYGEditor/index.ts +3 -0
  122. package/src/v2/components/index.ts +37 -0
  123. package/src/v2/index.ts +10 -2
  124. package/src/v2/navigation/DashboardLayout/TopNavigation.scss +1 -0
  125. package/src/v2/pages/Settings/SettingsContentPlaceholder.scss +24 -0
  126. package/src/v2/pages/Settings/SettingsPage.scss +52 -0
  127. package/src/v2/pages/Settings/SettingsPage.tsx +46 -0
  128. package/src/v2/pages/Settings/SettingsTabs.scss +44 -0
  129. package/src/v2/pages/Settings/SettingsTabs.tsx +36 -0
  130. package/src/v2/pages/Settings/components/SidebarCustomisation.stories.tsx +48 -0
  131. package/src/v2/pages/Settings/components/SidebarNavItem.scss +76 -0
  132. package/src/v2/pages/Settings/components/SidebarNavItem.stories.tsx +50 -0
  133. package/src/v2/pages/Settings/components/SidebarNavItem.tsx +52 -0
  134. package/src/v2/pages/Settings/components/index.ts +5 -0
  135. package/src/v2/pages/Settings/index.ts +8 -0
  136. package/src/v2/styles/components/Button.scss +51 -53
  137. package/src/v2/styles/form/Layout.scss +15 -0
  138. package/src/v2/styles/form/NewInput.scss +83 -53
  139. package/src/v2/styles/index.scss +1 -0
  140. package/src/v2/styles/tokens/_colors.scss +6 -6
  141. package/src/v2/styles/tokens/_typography.scss +2 -2
  142. package/dist/v2/navigation/icons/index.d.ts +0 -12
  143. package/dist/v2/navigation/icons/index.d.ts.map +0 -1
  144. package/src/v2/navigation/icons/index.tsx +0 -72
@@ -0,0 +1,393 @@
1
+ import React from 'react'
2
+ import {
3
+ BoldIcon,
4
+ ItalicIcon,
5
+ UnderlineIcon,
6
+ StrikeThroughIcon,
7
+ Heading1Icon,
8
+ Heading2Icon,
9
+ BulletListIcon,
10
+ NumberedListIcon,
11
+ UndoIcon,
12
+ RedoIcon,
13
+ ParagraphIcon,
14
+ QuoteAltIcon,
15
+ DividerIcon,
16
+ ClearIcon,
17
+ HardBreakIcon,
18
+ } from './Icons'
19
+ import './WYSIWYGEditor.scss'
20
+
21
+ export interface WYSIWYGEditorProps {
22
+ label?: string
23
+ error?: string
24
+ helpText?: string
25
+ value?: string
26
+ onChange?: (value: string) => void
27
+ onClear?: () => void
28
+ placeholder?: string
29
+ disabled?: boolean
30
+ }
31
+
32
+ export const WYSIWYGEditor: React.FC<WYSIWYGEditorProps> = ({
33
+ label,
34
+ error,
35
+ helpText,
36
+ value,
37
+ onChange,
38
+ placeholder,
39
+ disabled = false,
40
+ }) => {
41
+ const contentRef = React.useRef<HTMLDivElement>(null)
42
+ const isInternalChange = React.useRef(false)
43
+
44
+ // Only update innerHTML when value changes externally (not from user input)
45
+ React.useEffect(() => {
46
+ if (contentRef.current) {
47
+ if (isInternalChange.current) {
48
+ isInternalChange.current = false
49
+ return
50
+ }
51
+
52
+ const currentHTML = contentRef.current.innerHTML
53
+ if (currentHTML !== value) {
54
+ contentRef.current.innerHTML = value || ''
55
+ }
56
+ }
57
+ }, [value])
58
+
59
+ const handleInput = () => {
60
+ if (contentRef.current) {
61
+ isInternalChange.current = true
62
+ onChange?.(contentRef.current.innerHTML)
63
+ }
64
+ }
65
+
66
+ const execCommand = (command: string, value?: string) => {
67
+ // Ensure the command is only executed if the editor is focused or if we can force focus to it
68
+ const selection = window.getSelection()
69
+ if (!selection) return
70
+
71
+ let isWithinEditor = false
72
+ if (selection.rangeCount > 0) {
73
+ const range = selection.getRangeAt(0)
74
+ isWithinEditor = contentRef.current?.contains(range.commonAncestorContainer) || false
75
+ }
76
+
77
+ // If not within editor, we focus the editor first to ensure the command applies to it
78
+ if (!isWithinEditor && contentRef.current) {
79
+ contentRef.current.focus()
80
+
81
+ // We need to re-get the selection after focusing as it might have changed
82
+ const currentSelection = window.getSelection()
83
+ if (currentSelection) {
84
+ // If the editor is empty or we want to ensure cursor is inside, we set it
85
+ if (contentRef.current.innerHTML === '' || contentRef.current.innerHTML === '<p><br></p>') {
86
+ const newRange = document.createRange()
87
+ newRange.selectNodeContents(contentRef.current)
88
+ newRange.collapse(false)
89
+ currentSelection.removeAllRanges()
90
+ currentSelection.addRange(newRange)
91
+ }
92
+ }
93
+ }
94
+
95
+ if (command === 'clearFormatting') {
96
+ if (selection.rangeCount > 0) {
97
+ const range = selection.getRangeAt(0)
98
+
99
+ // Check if selection is inside a list
100
+ let container = range.commonAncestorContainer as Node | null
101
+ if (container.nodeType === Node.TEXT_NODE) {
102
+ container = container.parentNode
103
+ }
104
+
105
+ let isInsideList = false
106
+ let tempNode = container
107
+ while (tempNode && tempNode !== contentRef.current) {
108
+ if (tempNode.nodeName === 'UL' || tempNode.nodeName === 'OL') {
109
+ isInsideList = true
110
+ break
111
+ }
112
+ tempNode = tempNode.parentNode
113
+ }
114
+
115
+ if (isInsideList) {
116
+ // If inside a list, we need to toggle off the list first
117
+ // We check which type of list it is and call the corresponding command
118
+ let listType = '';
119
+ let listNode = container;
120
+ while (listNode && listNode !== contentRef.current) {
121
+ if (listNode.nodeName === 'UL') {
122
+ listType = 'insertUnorderedList';
123
+ break;
124
+ } else if (listNode.nodeName === 'OL') {
125
+ listType = 'insertOrderedList';
126
+ break;
127
+ }
128
+ listNode = listNode.parentNode;
129
+ }
130
+
131
+ if (listType) {
132
+ document.execCommand(listType, false);
133
+ }
134
+ }
135
+
136
+ // If the selection is collapsed, just apply to current block
137
+ if (range.collapsed) {
138
+ document.execCommand('formatBlock', false, '<p>')
139
+ document.execCommand('removeFormat', false)
140
+ } else {
141
+ // If there's a selection, we check if it contains HR
142
+ const fragment = range.cloneContents()
143
+ const hasHR = fragment.querySelector('hr')
144
+
145
+ if (hasHR) {
146
+ // If it contains HR, we apply removeFormat but avoid formatBlock P
147
+ // which would replace the whole selection including HR with a single P
148
+ document.execCommand('removeFormat', false)
149
+ } else {
150
+ document.execCommand('formatBlock', false, '<p>')
151
+ document.execCommand('removeFormat', false)
152
+ }
153
+ }
154
+ handleInput()
155
+ }
156
+ return
157
+ }
158
+
159
+ if (command === 'insertHorizontalRule') {
160
+ if (selection.rangeCount > 0) {
161
+ const range = selection.getRangeAt(0)
162
+ // range.deleteContents() // Removed to avoid clearing highlighted text
163
+
164
+ const hr = document.createElement('hr')
165
+ const p = document.createElement('p')
166
+ p.innerHTML = '<br>' // Ensure the paragraph is "typeable"
167
+
168
+ // If something is selected, we collapse to end to insert HR AFTER selection
169
+ if (!range.collapsed) {
170
+ range.collapse(false)
171
+ }
172
+
173
+ // Insert HR and then the paragraph after it
174
+ range.insertNode(p)
175
+ range.insertNode(hr)
176
+
177
+ // Move cursor to the new paragraph
178
+ const newRange = document.createRange()
179
+ newRange.setStart(p, 0)
180
+ newRange.setEnd(p, 0)
181
+ newRange.collapse(true)
182
+ selection.removeAllRanges()
183
+ selection.addRange(newRange)
184
+
185
+ handleInput()
186
+ return
187
+ }
188
+ }
189
+
190
+ if (command === 'hardBreak') {
191
+ if (selection.rangeCount > 0) {
192
+ const range = selection.getRangeAt(0)
193
+ // range.deleteContents() // Removed to avoid clearing highlighted text
194
+
195
+ const spacer = document.createElement('div')
196
+ spacer.className = 'wysiwyg-editor__hard-break'
197
+ spacer.innerHTML = '<br>'
198
+
199
+ // If something is selected, we collapse to end to insert break AFTER selection
200
+ // or we can just insert it at current position.
201
+ // User says "it should just break to new line", usually that implies keeping the text.
202
+ if (!range.collapsed) {
203
+ range.collapse(false)
204
+ }
205
+
206
+ range.insertNode(spacer)
207
+
208
+ // Create a new paragraph after the spacer to continue typing
209
+ const p = document.createElement('p')
210
+ p.innerHTML = '<br>'
211
+ spacer.after(p)
212
+
213
+ // Move cursor to the new paragraph
214
+ const newRange = document.createRange()
215
+ newRange.setStart(p, 0)
216
+ newRange.setEnd(p, 0)
217
+ newRange.collapse(true)
218
+ selection.removeAllRanges()
219
+ selection.addRange(newRange)
220
+
221
+ handleInput()
222
+ return
223
+ }
224
+ }
225
+ document.execCommand(command, false, value)
226
+ handleInput()
227
+ }
228
+
229
+ return (
230
+ <div className="wysiwyg-editor">
231
+ {label && (
232
+ <label className="wysiwyg-editor__label">
233
+ {label}
234
+ </label>
235
+ )}
236
+ <div className={`wysiwyg-editor__wrapper ${error ? 'wysiwyg-editor__wrapper--error' : ''}`}>
237
+ <div className="wysiwyg-editor__toolbar">
238
+ <button
239
+ type="button"
240
+ className="wysiwyg-editor__toolbar-btn"
241
+ title="Bold"
242
+ onClick={() => execCommand('bold')}
243
+ >
244
+ <BoldIcon />
245
+ </button>
246
+ <button
247
+ type="button"
248
+ className="wysiwyg-editor__toolbar-btn"
249
+ title="Italic"
250
+ onClick={() => execCommand('italic')}
251
+ >
252
+ <ItalicIcon />
253
+ </button>
254
+ <button
255
+ type="button"
256
+ className="wysiwyg-editor__toolbar-btn"
257
+ title="Underline"
258
+ onClick={() => execCommand('underline')}
259
+ >
260
+ <UnderlineIcon />
261
+ </button>
262
+ <button
263
+ type="button"
264
+ className="wysiwyg-editor__toolbar-btn"
265
+ title="Strikethrough"
266
+ onClick={() => execCommand('strikeThrough')}
267
+ >
268
+ <StrikeThroughIcon />
269
+ </button>
270
+ <button
271
+ type="button"
272
+ className="wysiwyg-editor__toolbar-btn"
273
+ title="Heading 1"
274
+ onClick={() => execCommand('formatBlock', '<h1>')}
275
+ >
276
+ <Heading1Icon />
277
+ </button>
278
+ <button
279
+ type="button"
280
+ className="wysiwyg-editor__toolbar-btn"
281
+ title="Heading 2"
282
+ onClick={() => execCommand('formatBlock', '<h2>')}
283
+ >
284
+ <Heading2Icon />
285
+ </button>
286
+ <button
287
+ type="button"
288
+ className="wysiwyg-editor__toolbar-btn"
289
+ title="Paragraph"
290
+ onClick={() => execCommand('clearFormatting')}
291
+ >
292
+ <ParagraphIcon />
293
+ </button>
294
+ <button
295
+ type="button"
296
+ className="wysiwyg-editor__toolbar-btn"
297
+ title="Quote"
298
+ onClick={() => execCommand('formatBlock', '<blockquote>')}
299
+ >
300
+ <QuoteAltIcon />
301
+ </button>
302
+ <button
303
+ type="button"
304
+ className="wysiwyg-editor__toolbar-btn"
305
+ title="Bullet List"
306
+ onClick={() => execCommand('insertUnorderedList')}
307
+ >
308
+ <BulletListIcon />
309
+ </button>
310
+ <button
311
+ type="button"
312
+ className="wysiwyg-editor__toolbar-btn"
313
+ title="Numbered List"
314
+ onClick={() => execCommand('insertOrderedList')}
315
+ >
316
+ <NumberedListIcon />
317
+ </button>
318
+ <button
319
+ type="button"
320
+ className="wysiwyg-editor__toolbar-btn"
321
+ title="Divider"
322
+ onClick={() => execCommand('insertHorizontalRule')}
323
+ >
324
+ <DividerIcon />
325
+ </button>
326
+ <button
327
+ type="button"
328
+ className="wysiwyg-editor__toolbar-btn"
329
+ title="Hard Break"
330
+ onClick={() => execCommand('hardBreak')}
331
+ >
332
+ <HardBreakIcon />
333
+ </button>
334
+ <button
335
+ type="button"
336
+ className="wysiwyg-editor__toolbar-btn"
337
+ title="Undo"
338
+ onClick={() => {
339
+ const selection = window.getSelection()
340
+ if (selection && contentRef.current && !contentRef.current.contains(selection.anchorNode)) {
341
+ contentRef.current.focus()
342
+ }
343
+ if (document.queryCommandEnabled('undo')) {
344
+ document.execCommand('undo', false)
345
+ handleInput()
346
+ }
347
+ }}
348
+ >
349
+ <UndoIcon />
350
+ </button>
351
+ <button
352
+ type="button"
353
+ className="wysiwyg-editor__toolbar-btn"
354
+ title="Redo"
355
+ onClick={() => {
356
+ const selection = window.getSelection()
357
+ if (selection && contentRef.current && !contentRef.current.contains(selection.anchorNode)) {
358
+ contentRef.current.focus()
359
+ }
360
+ if (document.queryCommandEnabled('redo')) {
361
+ document.execCommand('redo', false)
362
+ handleInput()
363
+ }
364
+ }}
365
+ >
366
+ <RedoIcon />
367
+ </button>
368
+ <button
369
+ type="button"
370
+ className="wysiwyg-editor__toolbar-btn"
371
+ title="Clear Formatting"
372
+ onClick={() => execCommand('clearFormatting')}
373
+ >
374
+ <ClearIcon />
375
+ </button>
376
+ </div>
377
+ <div
378
+ ref={contentRef}
379
+ className={`wysiwyg-editor__content ${disabled ? 'wysiwyg-editor__content--disabled' : ''}`}
380
+ contentEditable={!disabled}
381
+ suppressContentEditableWarning
382
+ onInput={handleInput}
383
+ data-placeholder={placeholder}
384
+ />
385
+ </div>
386
+ {helpText && (
387
+ <span className="wysiwyg-editor__help-text">{helpText}</span>
388
+ )}
389
+ {error && <span className="wysiwyg-editor__error-text">{error}</span>}
390
+ </div>
391
+ )
392
+ }
393
+
@@ -0,0 +1,3 @@
1
+ export { WYSIWYGEditor } from './WYSIWYGEditor'
2
+ export type { WYSIWYGEditorProps } from './WYSIWYGEditor'
3
+
@@ -0,0 +1,37 @@
1
+ // Form Components
2
+ export { FormField } from './FormField'
3
+ export type { FormFieldProps } from './FormField'
4
+
5
+ export { NewInput } from './NewInput'
6
+ export type { NewInputProps } from './NewInput'
7
+
8
+ export { Checkbox } from './Checkbox'
9
+ export type { CheckboxProps } from './Checkbox'
10
+
11
+ export { WYSIWYGEditor } from './WYSIWYGEditor'
12
+ export type { WYSIWYGEditorProps } from './WYSIWYGEditor'
13
+
14
+ export { NewPageHeader } from './NewPageHeader'
15
+ export type { NewPageHeaderProps } from './NewPageHeader'
16
+
17
+ export { SectionHeader } from './SectionHeader'
18
+ export type { SectionHeaderProps } from './SectionHeader'
19
+
20
+ // Existing Components
21
+ export { Button, ButtonText } from './Button'
22
+ export type { ButtonProps, ButtonTextProps } from './Button'
23
+
24
+ export { Select } from './Select'
25
+
26
+ export { Tooltip } from './Tooltip'
27
+
28
+ export { UserAvatar } from './UserAvatar'
29
+
30
+ export { UserPanel } from './UserPanel'
31
+
32
+ export { EntityHeader } from './EntityHeader'
33
+
34
+ export { Alert } from './Alert'
35
+
36
+ export { NPSScore } from './NPSScore'
37
+
package/src/v2/index.ts CHANGED
@@ -107,8 +107,8 @@ export type {
107
107
  // Navigation Configuration
108
108
  export { NAVIGATION_ITEMS } from './navigation/config'
109
109
 
110
- // Navigation Icons
111
- export * from './navigation/icons'
110
+ // Icons
111
+ export * from './icons'
112
112
 
113
113
  // ============================================================================
114
114
  // Hooks
@@ -122,6 +122,14 @@ export { useAuth, useCanAccess } from './hooks/useAuth'
122
122
 
123
123
  export type { AuthCredentials } from './types/navigation'
124
124
 
125
+ // ============================================================================
126
+ // Pages
127
+ // ============================================================================
128
+
129
+ // Settings Page
130
+ export { SettingsPage, SettingsTabs, SidebarCustomisation, defaultSidebarItems, SidebarNavItem } from './pages/Settings'
131
+ export type { SettingsPageProps, SettingsTabsProps, SettingsTab, SidebarCustomisationProps, SidebarItem, SidebarNavItemProps } from './pages/Settings'
132
+
125
133
  // ============================================================================
126
134
  // Design Tokens
127
135
  // ============================================================================
@@ -91,6 +91,7 @@
91
91
 
92
92
  @media (min-width: 640px) {
93
93
  display: block;
94
+ width: 116px;
94
95
  }
95
96
  }
96
97
 
@@ -0,0 +1,24 @@
1
+ @import '../../styles/tokens/typography';
2
+ @import '../../styles/tokens/colors';
3
+
4
+ .settings-content-placeholder {
5
+ padding: 24px 0;
6
+ width: 100%;
7
+ font-family: var(--font-family-sans);
8
+
9
+ &__title {
10
+ @include typography('heading.h2');
11
+ margin: 0 0 16px 0;
12
+ }
13
+
14
+ &__description {
15
+ @include typography('text.regular');
16
+ color: var(--label-secondary);
17
+ margin: 0;
18
+ }
19
+ }
20
+
21
+ .settings-tabs-standalone {
22
+ border-bottom: 1px solid var(--border-primary);
23
+ padding-bottom: 0;
24
+ }
@@ -0,0 +1,52 @@
1
+ @import '../../styles/tokens/typography';
2
+ @import '../../styles/tokens/colors';
3
+
4
+ .settings-page {
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: flex-start;
8
+ gap: 24px;
9
+ align-self: stretch;
10
+ font-family: var(--font-family-sans);
11
+
12
+ &__header {
13
+ display: flex;
14
+ flex-direction: column;
15
+ align-items: flex-start;
16
+ align-self: stretch;
17
+ border-bottom: 1px solid var(--border-primary);
18
+ }
19
+
20
+ &__title-row {
21
+ display: flex;
22
+ height: 72px;
23
+ justify-content: space-between;
24
+ align-items: center;
25
+ align-self: stretch;
26
+ padding: 24px 0 8px 8px;
27
+
28
+ @media (max-width: $bp-mobile-max) {
29
+ height: auto;
30
+ padding: 16px 0 8px 8px;
31
+ }
32
+ }
33
+
34
+ &__title {
35
+ @include typography('heading.h1');
36
+ margin: 0;
37
+
38
+ @media (max-width: $bp-mobile-max) {
39
+ @include typography('heading.h1.mobile');
40
+ }
41
+ }
42
+
43
+ &__content {
44
+ display: flex;
45
+ flex-direction: column;
46
+ align-items: stretch;
47
+ gap: 16px;
48
+ align-self: stretch;
49
+ width: 100%;
50
+ padding: 0 8px;
51
+ }
52
+ }
@@ -0,0 +1,46 @@
1
+ import React, { useState } from 'react'
2
+ import './SettingsPage.scss'
3
+ import { SettingsTabs, SettingsTab } from './SettingsTabs'
4
+
5
+ export interface SettingsPageProps {
6
+ title?: string
7
+ tabs: SettingsTab[]
8
+ defaultTab?: string
9
+ onTabChange?: (tabId: string) => void
10
+ children?: React.ReactNode
11
+ }
12
+
13
+ export const SettingsPage: React.FC<SettingsPageProps> = ({
14
+ title = 'Settings',
15
+ tabs,
16
+ defaultTab,
17
+ onTabChange,
18
+ children,
19
+ }) => {
20
+ const [activeTab, setActiveTab] = useState(defaultTab || tabs[0]?.id || '')
21
+
22
+ const handleTabChange = (tabId: string) => {
23
+ setActiveTab(tabId)
24
+ onTabChange?.(tabId)
25
+ }
26
+
27
+ return (
28
+ <div className="settings-page">
29
+ <header className="settings-page__header">
30
+ <div className="settings-page__title-row">
31
+ <h1 className="settings-page__title">{title}</h1>
32
+ </div>
33
+ <SettingsTabs
34
+ tabs={tabs}
35
+ activeTab={activeTab}
36
+ onTabChange={handleTabChange}
37
+ />
38
+ </header>
39
+ <div className="settings-page__content">
40
+ {children}
41
+ </div>
42
+ </div>
43
+ )
44
+ }
45
+
46
+ export default SettingsPage
@@ -0,0 +1,44 @@
1
+ @import '../../styles/tokens/typography';
2
+ @import '../../styles/tokens/colors';
3
+
4
+ .settings-tabs {
5
+ display: flex;
6
+ align-items: flex-start;
7
+ gap: 24px;
8
+ align-self: stretch;
9
+ font-family: var(--font-family-sans);
10
+
11
+ &__tab {
12
+ display: flex;
13
+ padding: 12px 0;
14
+ justify-content: center;
15
+ align-items: center;
16
+ gap: 8px;
17
+ background: none;
18
+ border: none;
19
+ border-bottom: 2px solid transparent;
20
+ cursor: pointer;
21
+ transition: border-color 0.2s ease, color 0.2s ease;
22
+
23
+ &:hover {
24
+ .settings-tabs__tab-label {
25
+ color: var(--label-primary);
26
+ }
27
+ }
28
+
29
+ &--active {
30
+ border-bottom-color: var(--fill-primary);
31
+
32
+ .settings-tabs__tab-label {
33
+ color: var(--label-primary);
34
+ font-weight: 600;
35
+ }
36
+ }
37
+ }
38
+
39
+ &__tab-label {
40
+ @include typography('text.regular');
41
+ color: var(--label-secondary);
42
+ transition: color 0.2s ease;
43
+ }
44
+ }
@@ -0,0 +1,36 @@
1
+ import React from 'react'
2
+ import './SettingsTabs.scss'
3
+
4
+ export interface SettingsTab {
5
+ id: string
6
+ label: string
7
+ }
8
+
9
+ export interface SettingsTabsProps {
10
+ tabs: SettingsTab[]
11
+ activeTab: string
12
+ onTabChange: (tabId: string) => void
13
+ }
14
+
15
+ export const SettingsTabs: React.FC<SettingsTabsProps> = ({
16
+ tabs,
17
+ activeTab,
18
+ onTabChange,
19
+ }) => {
20
+ return (
21
+ <nav className="settings-tabs">
22
+ {tabs.map((tab) => (
23
+ <button
24
+ key={tab.id}
25
+ onClick={() => onTabChange(tab.id)}
26
+ className={`settings-tabs__tab ${activeTab === tab.id ? 'settings-tabs__tab--active' : ''}`}
27
+ >
28
+ <span className="settings-tabs__label">{tab.label}</span>
29
+ <div className="settings-tabs__indicator" />
30
+ </button>
31
+ ))}
32
+ </nav>
33
+ )
34
+ }
35
+
36
+ export default SettingsTabs