@licklist/design 0.78.5-dev.111 → 0.78.5-dev.112

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 (80) hide show
  1. package/dist/index.js +2 -0
  2. package/dist/v2/components/AccountManagerCard/AccountManagerCard.scss.js +1 -1
  3. package/dist/v2/components/ActionMenu/ActionMenu.scss.js +1 -1
  4. package/dist/v2/components/Alert/Alert.scss.js +1 -1
  5. package/dist/v2/components/AvatarUpload/AvatarUpload.scss.js +1 -1
  6. package/dist/v2/components/Badge/Badge.scss.js +1 -1
  7. package/dist/v2/components/Checkbox/Checkbox.d.ts.map +1 -1
  8. package/dist/v2/components/Checkbox/Checkbox.js +10 -50
  9. package/dist/v2/components/Checkbox/Checkbox.scss.js +1 -1
  10. package/dist/v2/components/ColorPicker/ColorPicker.d.ts +15 -0
  11. package/dist/v2/components/ColorPicker/ColorPicker.d.ts.map +1 -0
  12. package/dist/v2/components/ColorPicker/ColorPicker.js +40 -0
  13. package/dist/v2/components/ColorPicker/ColorPicker.scss.js +6 -0
  14. package/dist/v2/components/ColorPicker/index.d.ts +2 -0
  15. package/dist/v2/components/ColorPicker/index.d.ts.map +1 -0
  16. package/dist/v2/components/DataTable/DataTable.scss.js +1 -1
  17. package/dist/v2/components/EmptyState/EmptyState.scss.js +1 -1
  18. package/dist/v2/components/FeatureCard/FeatureCard.d.ts +11 -0
  19. package/dist/v2/components/FeatureCard/FeatureCard.d.ts.map +1 -0
  20. package/dist/v2/components/FeatureCard/FeatureCard.js +40 -0
  21. package/dist/v2/components/FeatureCard/FeatureCard.scss.js +6 -0
  22. package/dist/v2/components/FeatureCard/index.d.ts +3 -0
  23. package/dist/v2/components/FeatureCard/index.d.ts.map +1 -0
  24. package/dist/v2/components/FeatureToggle/FeatureToggle.scss.js +1 -1
  25. package/dist/v2/components/FormField/FormField.scss.js +1 -1
  26. package/dist/v2/components/IconButton/IconButton.d.ts +5 -1
  27. package/dist/v2/components/IconButton/IconButton.d.ts.map +1 -1
  28. package/dist/v2/components/IconButton/IconButton.js +28 -3
  29. package/dist/v2/components/IconButton/IconButton.scss.js +1 -1
  30. package/dist/v2/components/InfoGrid/InfoGrid.scss.js +1 -1
  31. package/dist/v2/components/NotificationBanner/NotificationBanner.scss.js +1 -1
  32. package/dist/v2/components/PeriodCard/PeriodCard.d.ts +18 -4
  33. package/dist/v2/components/PeriodCard/PeriodCard.d.ts.map +1 -1
  34. package/dist/v2/components/PeriodCard/PeriodCard.js +46 -6
  35. package/dist/v2/components/StatusBadge/StatusBadge.scss.js +1 -1
  36. package/dist/v2/components/TableControls/TableControls.scss.js +1 -1
  37. package/dist/v2/components/Tabs/Tabs.scss.js +1 -1
  38. package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.scss.js +1 -1
  39. package/dist/v2/components/index.d.ts +4 -0
  40. package/dist/v2/components/index.d.ts.map +1 -1
  41. package/dist/v2/icons/index.d.ts +19 -2
  42. package/dist/v2/icons/index.d.ts.map +1 -1
  43. package/dist/v2/icons/index.js +21 -4
  44. package/dist/v2/pages/Settings/SettingsPage.scss.js +1 -1
  45. package/dist/v2/pages/Settings/SettingsTabs.scss.js +1 -1
  46. package/dist/v2/pages/Settings/components/SidebarCustomisation.js +2 -0
  47. package/dist/v2/pages/Settings/components/SidebarCustomisation.scss.js +1 -1
  48. package/dist/v2/pages/Settings/components/SidebarNavItem.js +2 -0
  49. package/dist/v2/pages/Settings/components/SidebarNavItem.scss.js +1 -1
  50. package/dist/v2/pages/SettingsSubPage/SettingsSubPage.d.ts +1 -0
  51. package/dist/v2/pages/SettingsSubPage/SettingsSubPage.d.ts.map +1 -1
  52. package/dist/v2/pages/SettingsSubPage/SettingsSubPage.js +2 -2
  53. package/dist/v2/pages/SettingsSubPage/SettingsSubPage.scss.js +1 -1
  54. package/dist/v2/styles/tokens/_colors.scss +19 -0
  55. package/dist/v2/utils/colourPicker/colors.d.ts +8 -0
  56. package/dist/v2/utils/colourPicker/colors.d.ts.map +1 -0
  57. package/dist/v2/utils/timeFormat/formatTime.d.ts +14 -0
  58. package/dist/v2/utils/timeFormat/formatTime.d.ts.map +1 -0
  59. package/package.json +3 -3
  60. package/src/v2/components/Badge/Badge.scss +1 -2
  61. package/src/v2/components/Checkbox/Checkbox.scss +37 -57
  62. package/src/v2/components/Checkbox/Checkbox.tsx +6 -44
  63. package/src/v2/components/ColorPicker/ColorPicker.scss +96 -0
  64. package/src/v2/components/ColorPicker/ColorPicker.stories.tsx +68 -0
  65. package/src/v2/components/ColorPicker/ColorPicker.tsx +49 -0
  66. package/src/v2/components/ColorPicker/index.ts +1 -0
  67. package/src/v2/components/FeatureCard/FeatureCard.scss +65 -0
  68. package/src/v2/components/FeatureCard/FeatureCard.stories.tsx +42 -0
  69. package/src/v2/components/FeatureCard/FeatureCard.tsx +37 -0
  70. package/src/v2/components/FeatureCard/index.ts +2 -0
  71. package/src/v2/components/IconButton/IconButton.scss +1 -1
  72. package/src/v2/components/IconButton/IconButton.tsx +41 -2
  73. package/src/v2/components/PeriodCard/PeriodCard.tsx +76 -13
  74. package/src/v2/components/index.ts +6 -0
  75. package/src/v2/icons/index.tsx +29 -4
  76. package/src/v2/pages/SettingsSubPage/SettingsSubPage.scss +4 -4
  77. package/src/v2/pages/SettingsSubPage/SettingsSubPage.tsx +3 -1
  78. package/src/v2/styles/tokens/_colors.scss +19 -0
  79. package/src/v2/utils/colourPicker/colors.ts +17 -0
  80. package/src/v2/utils/timeFormat/formatTime.ts +18 -0
@@ -7,7 +7,7 @@ import { ArrowLeftIcon, PlusIcon } from '../../icons/index.js';
7
7
  import './SettingsSubPage.scss.js';
8
8
 
9
9
  var SettingsSubPage = function(param) {
10
- var _param_title = param.title, title = _param_title === void 0 ? '' : _param_title, _param_backLabel = param.backLabel, backLabel = _param_backLabel === void 0 ? 'Back to Settings' : _param_backLabel, onBack = param.onBack, actionLabel = param.actionLabel, onAction = param.onAction, children = param.children;
10
+ var _param_title = param.title, title = _param_title === void 0 ? '' : _param_title, _param_backLabel = param.backLabel, backLabel = _param_backLabel === void 0 ? 'Back to Settings' : _param_backLabel, onBack = param.onBack, actionLabel = param.actionLabel, onAction = param.onAction, children = param.children, _param_showDivider = param.showDivider, showDivider = _param_showDivider === void 0 ? true : _param_showDivider;
11
11
  return /*#__PURE__*/ jsxs("div", {
12
12
  className: "settings-sub-page",
13
13
  children: [
@@ -21,7 +21,7 @@ var SettingsSubPage = function(param) {
21
21
  }),
22
22
  /*#__PURE__*/ jsx(NewPageHeader, {
23
23
  title: title,
24
- showDivider: true
24
+ showDivider: showDivider
25
25
  }),
26
26
  /*#__PURE__*/ jsxs("div", {
27
27
  className: "settings-sub-page__content",
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".settings-sub-page{align-self:stretch;display:flex;flex-direction:column}.settings-sub-page__back{align-self:flex-start;padding:24px 32px 0 0}.settings-sub-page__back .ghost-button{border-color:var(--borders-main-border-primary,#e8e9ef);color:var(--fill-action,#5d5bf4)}.settings-sub-page__back .ghost-button:not(.active):hover:not(:disabled){background-color:var(--surface-action-soft,#f4f4fe);color:var(--fill-action,#5d5bf4)}.settings-sub-page__back .ghost-button .ghost-button__icon svg{height:24px;width:24px}@media (max-width:768px){.settings-sub-page__back{padding:16px 16px 0 0}}.settings-sub-page__content{padding:24px 0}@media (max-width:768px){.settings-sub-page__content{padding:0}}.settings-sub-page__action{display:flex;justify-content:flex-end;margin-bottom:16px}";
3
+ var css_248z = ".settings-sub-page{align-self:stretch;display:flex;flex-direction:column}.settings-sub-page__back{align-self:flex-start;padding:24px 0 0}.settings-sub-page__back .ghost-button{border-color:var(--borders-main-border-primary,#e8e9ef);color:var(--fill-action,#5d5bf4)}.settings-sub-page__back .ghost-button:not(.active):hover:not(:disabled){background-color:var(--surface-action-soft,#f4f4fe);color:var(--fill-action,#5d5bf4)}.settings-sub-page__back .ghost-button .ghost-button__icon svg{height:24px;width:24px}@media (max-width:768px){.settings-sub-page__back{padding:16px 0 0}}.settings-sub-page__content{padding:0}@media (max-width:768px){.settings-sub-page__content{padding:16px 0}}.settings-sub-page__action{display:flex;justify-content:flex-end;margin-bottom:16px}";
4
4
  styleInject(css_248z);
5
5
 
6
6
  export { css_248z as default };
@@ -116,6 +116,16 @@
116
116
  --pink-800: #80224D;
117
117
  --pink-900: #611A3B;
118
118
 
119
+ --grey-100: #F8F8FA;
120
+ --grey-200: #E8E9EF;
121
+ --grey-300: #D2D5E3;
122
+ --grey-400: #B6BACC;
123
+ --grey-500: #9399B3;
124
+ --grey-600: #626A90;
125
+ --grey-700: #433D7B;
126
+ --grey-800: #14215A;
127
+ --grey-900: #0B1232;
128
+
119
129
  /* Teals */
120
130
  --teal-50: #E9FAF7;
121
131
  --teal-100: #BAF0E7;
@@ -218,6 +228,15 @@
218
228
  --pink-darker: var(--pink-700);
219
229
  --pink-darkest: var(--pink-800);
220
230
 
231
+ /* Grey Scale */
232
+ --grey-lightest: var(--grey-50);
233
+ --grey-lighter: var(--grey-100);
234
+ --grey-light: var(--grey-300);
235
+ --grey-regular: var(--grey-500);
236
+ --grey-dark: var(--grey-600);
237
+ --grey-darker: var(--grey-700);
238
+ --grey-darkest: var(--grey-800);
239
+
221
240
  /* Green Scale */
222
241
  --green-lightest: var(--green-50);
223
242
  --green-lighter: var(--green-100);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Standard color palette for pricing periods and other color picker components
3
+ */
4
+ export declare const COLORS: {
5
+ name: string;
6
+ hex: string;
7
+ }[];
8
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../../../src/v2/utils/colourPicker/colors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,MAAM;;;GAYlB,CAAA"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Formats time from "HH:mm:ss" to "HH:mm" format
3
+ * Useful for converting HTML time input values to backend-compatible format
4
+ *
5
+ * @param time - Time string in "HH:mm:ss" or "HH:mm" format
6
+ * @returns Time string in "HH:mm" format, or undefined if input is undefined
7
+ *
8
+ * @example
9
+ * formatTimeToHHMM("09:00:00") // returns "09:00"
10
+ * formatTimeToHHMM("17:30") // returns "17:30"
11
+ * formatTimeToHHMM(undefined) // returns undefined
12
+ */
13
+ export declare const formatTimeToHHMM: (time?: string) => string | undefined;
14
+ //# sourceMappingURL=formatTime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatTime.d.ts","sourceRoot":"","sources":["../../../../src/v2/utils/timeFormat/formatTime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAIzD,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@licklist/design",
3
- "version": "0.78.5-dev.111",
3
+ "version": "0.78.5-dev.112",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@bitbucket.org/bookedit-licklist/licklist_design.git"
@@ -42,7 +42,7 @@
42
42
  ]
43
43
  },
44
44
  "peerDependencies": {
45
- "@licklist/core": "0.36.1-dev.49",
45
+ "@licklist/core": "0.36.1-dev.52",
46
46
  "@licklist/eslint-config": "0.5.6",
47
47
  "@licklist/plugins": "0.36.4-dev.18",
48
48
  "clsx": "2.1.1",
@@ -65,7 +65,7 @@
65
65
  "@fortawesome/fontawesome-svg-core": "1.2.34",
66
66
  "@fortawesome/free-solid-svg-icons": "5.15.2",
67
67
  "@hookform/resolvers": "^5.2.2",
68
- "@licklist/core": "0.36.1-dev.49",
68
+ "@licklist/core": "0.36.1-dev.52",
69
69
  "@licklist/eslint-config": "0.5.6",
70
70
  "@licklist/plugins": "0.36.4-dev.18",
71
71
  "@mantine/core": "6.0.22",
@@ -1,5 +1,5 @@
1
1
  .new-badge {
2
- font-family: var(--font-family-mono, 'Geist Mono', monospace);
2
+ font-family: var(--font-family-sans, Geist, sans-serif);
3
3
  display: inline-flex;
4
4
  padding: 6px 10px;
5
5
  justify-content: center;
@@ -8,7 +8,6 @@
8
8
  color: var(--label-primary, #121E52);
9
9
  font-size: 11px;
10
10
  font-style: normal;
11
- font-weight: 600;
12
11
  line-height: 14px;
13
12
  height: 28px;
14
13
 
@@ -28,10 +28,10 @@
28
28
  width: 0;
29
29
 
30
30
  &:checked ~ .checkbox__toggle {
31
- background: var(--fill-selected, #14215A);
32
- border: 1px solid var(--border-primary, #E8E9EF);
31
+ background: var(--fill-selected, var(--neutral-500));
32
+ border: none;
33
33
 
34
- .checkbox__xmark {
34
+ .checkbox__unchecked-group {
35
35
  opacity: 0;
36
36
  visibility: hidden;
37
37
  }
@@ -43,29 +43,25 @@
43
43
  }
44
44
 
45
45
  &:focus ~ .checkbox__toggle {
46
- box-shadow: 0 0 0 2px rgba(20, 33, 90, 0.2);
46
+ outline: none;
47
47
  }
48
48
 
49
49
  &:disabled ~ .checkbox__toggle {
50
- background-color: var(--surface-status-disabled, #f8f8fa);
51
- border-color: var(--border-primary, #e8e9ef);
50
+ background-color: var(--surface-status-disabled, var(--neutral-25));
51
+ border-color: var(--border-primary, var(--neutral-50));
52
52
  cursor: not-allowed;
53
53
  opacity: 0.6;
54
54
  }
55
55
  }
56
56
 
57
- // Only show X mark when not pristine and not checked
57
+ // Only show Unchecked group (X) when not pristine and not checked
58
58
  &:not(&--pristine) &__input:not(:checked) ~ &__toggle {
59
- .checkbox__xmark {
59
+ background: var(--grey-500); // Match image track color
60
+ border: none;
61
+
62
+ .checkbox__unchecked-group {
60
63
  opacity: 1;
61
64
  visibility: visible;
62
- top: 50%;
63
- position: absolute;
64
- right: 4px;
65
- transform: translateY(-50%);
66
- display: flex;
67
- align-items: center;
68
- justify-content: center;
69
65
  }
70
66
 
71
67
  .checkbox__checked-group {
@@ -78,30 +74,18 @@
78
74
  position: relative;
79
75
  display: flex;
80
76
  align-items: center;
81
- justify-content: flex-end; // Positions X mark to the right
82
77
  width: 58px;
83
- height: 34px; // Set explicit height to help with vertical centering
78
+ height: 34px;
84
79
  min-width: 58px;
85
80
  padding: 4px;
86
- background: var(--surface-secondary, #F8F8FA);
87
- border: 1px solid var(--border-primary, #E8E9EF);
81
+ background: var(--surface-secondary, var(--neutral-25));
82
+ border: none;
88
83
  border-radius: var(--padding-xl, 32px);
89
84
  flex-shrink: 0;
85
+ transition: background-color 0.2s ease;
90
86
  }
91
87
 
92
- &__xmark {
93
- width: 24px;
94
- height: 24px;
95
- opacity: 0; // Hidden by default (blank state)
96
- visibility: hidden;
97
-
98
- svg {
99
- width: 100%;
100
- height: 100%;
101
- }
102
- }
103
-
104
- &__checked-group {
88
+ &__unchecked-group {
105
89
  position: absolute;
106
90
  left: 4px;
107
91
  top: 50%;
@@ -113,37 +97,33 @@
113
97
  display: flex;
114
98
  align-items: center;
115
99
  justify-content: center;
100
+ background: white;
101
+ border-radius: 50%;
116
102
  }
117
103
 
118
- &__circle {
119
- position: relative;
120
- width: 24px;
121
- height: 24px;
122
- display: flex;
123
- align-items: center;
124
- justify-content: center;
125
-
126
- svg {
127
- width: 100%;
128
- height: 100%;
129
- }
104
+ &__icon-cross {
105
+ fill: var(--grey-500);
106
+ opacity: 0.5;
130
107
  }
131
108
 
132
- &__checkmark {
109
+ &__checked-group {
133
110
  position: absolute;
111
+ right: 4px; // Checked position on the right
134
112
  top: 50%;
135
- left: 50%;
136
- transform: translate(-50%, -50%);
113
+ transform: translateY(-50%);
137
114
  width: 24px;
138
115
  height: 24px;
116
+ opacity: 0;
117
+ visibility: hidden;
139
118
  display: flex;
140
119
  align-items: center;
141
120
  justify-content: center;
121
+ background: white;
122
+ border-radius: 50%;
123
+ }
142
124
 
143
- svg {
144
- width: 100%;
145
- height: 100%;
146
- }
125
+ &__icon-tick {
126
+ fill: var(--neutral-500);
147
127
  }
148
128
 
149
129
  &__content {
@@ -177,19 +157,19 @@
177
157
  font-family: var(--font-family-sans, 'Geist', sans-serif), serif;
178
158
  font-size: var(--text-xs-size, 11px);
179
159
  line-height: var(--text-xs-line, 14px);
180
- color: var(--label-status-error, #ef4444);
160
+ color: var(--label-status-error, var(--red-500));
181
161
  margin-left: 69px;
182
162
  }
183
163
 
184
164
  &__wrapper:hover &__input:not(:disabled) ~ &__toggle {
185
- border-color: var(--border-primary-hover, #d1d3de);
165
+ // border-color: var(--border-primary-hover, #d1d3de);
186
166
  }
187
167
 
188
168
  &--pristine {
189
169
  .checkbox__toggle {
190
- background: var(--surface-secondary, #F8F8FA);
170
+ background: var(--surface-secondary, var(--neutral-25));
191
171
  }
192
- .checkbox__xmark,
172
+ .checkbox__unchecked-group,
193
173
  .checkbox__checked-group {
194
174
  opacity: 0 !important;
195
175
  visibility: hidden !important;
@@ -205,7 +185,7 @@
205
185
  align-items: center;
206
186
  gap: 8px;
207
187
  border-radius: var(--padding-xl, 32px);
208
- border: 1px solid var(--border-primary, #E8E9EF);
209
- background: var(--surface-primary, #FFF);
188
+ border: 1px solid var(--border-primary, var(--neutral-50));
189
+ background: var(--surface-primary, var(--neutral-white));
210
190
  }
211
191
 
@@ -1,4 +1,5 @@
1
1
  import React, { InputHTMLAttributes, forwardRef } from 'react'
2
+ import { IconTick, IconCross } from '../../icons'
2
3
  import './Checkbox.scss'
3
4
 
4
5
  export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
@@ -41,53 +42,14 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
41
42
  {...props}
42
43
  />
43
44
  <span className="checkbox__toggle">
44
- {/* X mark - visible when unchecked, positioned to the right */}
45
- <span className="checkbox__xmark">
46
- <svg
47
- xmlns="http://www.w3.org/2000/svg"
48
- width="24"
49
- height="24"
50
- viewBox="0 0 24 24"
51
- fill="none"
52
- >
53
- <path
54
- d="M0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12Z"
55
- fill="#14215A"
56
- />
57
- <path
58
- d="M18.1426 8.59961L14.0762 12.375L18.1426 16.1504L16.1006 18.3496L11.8711 14.4219L7.64258 18.3496L5.60059 16.1504L9.66602 12.375L5.60059 8.59961L7.64258 6.40039L11.8711 10.3271L16.1006 6.40039L18.1426 8.59961Z"
59
- fill="white"
60
- />
61
- </svg>
45
+ {/* White circle + X mark - visible when unchecked, positioned to the left */}
46
+ <span className="checkbox__unchecked-group">
47
+ <IconCross size={16} className="checkbox__icon-cross" />
62
48
  </span>
63
49
 
64
- {/* White circle + Checkmark - visible when checked, positioned to the left */}
50
+ {/* White circle + Checkmark - visible when checked, positioned to the right */}
65
51
  <span className="checkbox__checked-group">
66
- <span className="checkbox__circle">
67
- <svg
68
- xmlns="http://www.w3.org/2000/svg"
69
- width="24"
70
- height="24"
71
- viewBox="0 0 24 24"
72
- fill="none"
73
- >
74
- <circle cx="12" cy="12" r="12" fill="white" />
75
- </svg>
76
- </span>
77
- <span className="checkbox__checkmark">
78
- <svg
79
- xmlns="http://www.w3.org/2000/svg"
80
- width="24"
81
- height="24"
82
- viewBox="0 0 24 24"
83
- fill="none"
84
- >
85
- <path
86
- d="M19.1272 8.67725L10.8545 18.1013L5.71509 13.2058L7.78491 11.0334L10.6611 13.7734L16.8728 6.69824L19.1272 8.67725Z"
87
- fill="#14215A"
88
- />
89
- </svg>
90
- </span>
52
+ <IconTick size={16} className="checkbox__icon-tick" />
91
53
  </span>
92
54
  </span>
93
55
  {(label || description) && (
@@ -0,0 +1,96 @@
1
+ .color-picker-grid {
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ column-gap: 20px;
5
+ row-gap: 32px;
6
+ margin-top: 16px;
7
+ width: 100%;
8
+ border-radius: 8px;
9
+ border: 1px solid transparent;
10
+ transition: all 0.2s;
11
+
12
+ &--error {
13
+ border-color: #E0423A;
14
+ background-color: rgba(224, 66, 58, 0.05);
15
+ }
16
+
17
+ @media (max-width: 480px) {
18
+ column-gap: 0;
19
+ row-gap: 16px;
20
+ justify-content: flex-start;
21
+ }
22
+ }
23
+
24
+ .color-picker-button {
25
+ display: flex;
26
+ flex-direction: column;
27
+ align-items: center;
28
+ gap: 6px; // 1.5 * 4 = 6px
29
+ padding: 8px; // 2 * 4 = 8px
30
+ border-radius: 8px;
31
+ transition: background-color 0.2s;
32
+ cursor: pointer;
33
+ min-width: 72px;
34
+ background: transparent;
35
+ border: none;
36
+ outline: none;
37
+
38
+ @media (max-width: 480px) {
39
+ min-width: unset;
40
+ width: 25%;
41
+ padding: 4px;
42
+
43
+ .color-picker-outer-circle {
44
+ width: 48px;
45
+ height: 48px;
46
+ }
47
+
48
+ .color-picker-inner-circle {
49
+ width: 32px;
50
+ height: 32px;
51
+ }
52
+
53
+ .color-picker-name {
54
+ font-size: 10px;
55
+ }
56
+ }
57
+
58
+ &:hover {
59
+ background-color: var(--surface-primary-hover);
60
+ }
61
+ }
62
+
63
+ .color-picker-outer-circle {
64
+ position: relative;
65
+ width: 54px;
66
+ height: 54px;
67
+ border-radius: 50%;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ border: 1px solid var(--border-primary);
72
+ transition: border-color 0.2s;
73
+
74
+ &:hover {
75
+ border-color: var(--label-secondary);
76
+ }
77
+
78
+ &--selected {
79
+ border-width: 2px;
80
+ }
81
+ }
82
+
83
+ .color-picker-inner-circle {
84
+ width: 38px;
85
+ height: 38px;
86
+ border-radius: 50%;
87
+ transition: all 0.2s ease-out;
88
+ }
89
+
90
+ .color-picker-name {
91
+ font-size: 12px; // text-xs
92
+ font-weight: 500; // font-medium
93
+ color: var(--label-secondary);
94
+ line-height: 1.25; // leading-tight
95
+ text-transform: capitalize;
96
+ }
@@ -0,0 +1,68 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { ColorPicker } from './ColorPicker'
3
+ import * as React from 'react'
4
+ import { useState } from 'react'
5
+
6
+ const meta: Meta<typeof ColorPicker> = {
7
+ title: 'V2/Components/ColorPicker',
8
+ component: ColorPicker,
9
+ parameters: {
10
+ layout: 'centered',
11
+ },
12
+ tags: ['autodocs'],
13
+ }
14
+
15
+ export default meta
16
+ type Story = StoryObj<typeof ColorPicker>
17
+
18
+ const MOCK_COLORS = [
19
+ { name: 'Red', hex: '#EF4444' },
20
+ { name: 'Orange', hex: '#F97316' },
21
+ { name: 'Amber', hex: '#F59E0B' },
22
+ { name: 'Yellow', hex: '#EAB308' },
23
+ { name: 'Lime', hex: '#84CC16' },
24
+ { name: 'Green', hex: '#22C55E' },
25
+ { name: 'Emerald', hex: '#10B981' },
26
+ { name: 'Teal', hex: '#14B8A6' },
27
+ { name: 'Cyan', hex: '#06B6D4' },
28
+ { name: 'Sky', hex: '#0EA5E9' },
29
+ { name: 'Blue', hex: '#3B82F6' },
30
+ ]
31
+
32
+ export const Default: Story = {
33
+ args: {
34
+ colors: MOCK_COLORS,
35
+ selectedColor: '#EF4444',
36
+ onChange: (hex: string) => console.log('Selected color:', hex),
37
+ t: (key: string) => key,
38
+ },
39
+ }
40
+
41
+ export const Controlled: Story = {
42
+ render: (args) => {
43
+ const ControlledExample = () => {
44
+ const [selected, setSelected] = useState('#3B82F6')
45
+ return (
46
+ <ColorPicker
47
+ {...args}
48
+ selectedColor={selected}
49
+ onChange={setSelected}
50
+ t={(key: string) => key}
51
+ />
52
+ )
53
+ }
54
+ return <ControlledExample />
55
+ },
56
+ args: {
57
+ colors: MOCK_COLORS,
58
+ },
59
+ }
60
+
61
+ export const NoSelection: Story = {
62
+ args: {
63
+ colors: MOCK_COLORS,
64
+ selectedColor: '',
65
+ onChange: (hex: string) => console.log('Selected color:', hex),
66
+ t: (key: string) => key,
67
+ },
68
+ }
@@ -0,0 +1,49 @@
1
+ import React from 'react'
2
+ import './ColorPicker.scss'
3
+
4
+ export interface ColorOption {
5
+ name: string
6
+ hex: string
7
+ }
8
+
9
+ export interface ColorPickerProps {
10
+ colors: ColorOption[]
11
+ selectedColor?: string
12
+ onChange: (hex: string) => void
13
+ t: (key: string) => string
14
+ error?: string
15
+ }
16
+
17
+ export const ColorPicker: React.FC<ColorPickerProps> = ({
18
+ colors,
19
+ selectedColor,
20
+ onChange,
21
+ t,
22
+ error,
23
+ }) => {
24
+ return (
25
+ <div className={`color-picker-grid ${error ? 'color-picker-grid--error' : ''}`}>
26
+ {colors.map((color) => (
27
+ <button
28
+ key={color.name}
29
+ type="button"
30
+ className="color-picker-button"
31
+ onClick={() => onChange(color.hex)}
32
+ >
33
+ <div
34
+ className={`color-picker-outer-circle ${
35
+ selectedColor === color.hex ? 'color-picker-outer-circle--selected' : ''
36
+ }`}
37
+ style={selectedColor === color.hex ? { borderColor: color.hex, borderWidth: '2px' } : {}}
38
+ >
39
+ <div
40
+ className="color-picker-inner-circle"
41
+ style={{ backgroundColor: color.hex }}
42
+ />
43
+ </div>
44
+ <span className="color-picker-name">{t(color.name)}</span>
45
+ </button>
46
+ ))}
47
+ </div>
48
+ )
49
+ }
@@ -0,0 +1 @@
1
+ export * from './ColorPicker'
@@ -0,0 +1,65 @@
1
+ @import '../../styles/tokens/colors';
2
+ @import '../../styles/tokens/sizes';
3
+
4
+ .feature-card {
5
+ font-family: var(--font-family-sans, 'Geist', sans-serif), serif;
6
+ display: flex;
7
+ flex-direction: column;
8
+ justify-content: space-between;
9
+ gap: 20px;
10
+ padding: 12px;
11
+ background-color: var(--surface-secondary);
12
+ border: 1px solid var(--border-primary);
13
+ border-radius: 12px;
14
+
15
+ @media (min-width: 640px) {
16
+ flex-direction: row;
17
+ align-items: center;
18
+ }
19
+
20
+ &__content {
21
+ display: flex;
22
+ align-items: center;
23
+ gap: 20px;
24
+ flex: 1;
25
+ }
26
+
27
+ &__icon-wrapper {
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ flex-shrink: 0;
32
+ }
33
+
34
+ &__text {
35
+ display: flex;
36
+ flex-direction: column;
37
+ flex: 1;
38
+ }
39
+
40
+ &__title {
41
+ font-size: 16px;
42
+ line-height: 24px;
43
+ font-weight: 500;
44
+ color: var(--label-primary);
45
+ margin: 0;
46
+ }
47
+
48
+ &__description {
49
+ font-size: 14px;
50
+ line-height: 20px;
51
+ color: var(--label-primary);
52
+ opacity: 0.8;
53
+ margin: 4px 0 0;
54
+ }
55
+
56
+ &__action {
57
+ display: flex;
58
+ align-items: center;
59
+ width: 100%;
60
+
61
+ @media (min-width: 640px) {
62
+ width: auto;
63
+ }
64
+ }
65
+ }