@licklist/design 0.78.45 → 0.78.47
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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/v2/components/Button/Button.d.ts +1 -1
- package/dist/v2/components/Button/Button.d.ts.map +1 -1
- package/dist/v2/components/Button/Button.scss.js +1 -1
- package/dist/v2/components/PeriodCard/PeriodCard.d.ts +66 -0
- package/dist/v2/components/PeriodCard/PeriodCard.d.ts.map +1 -0
- package/dist/v2/components/PeriodCard/PeriodCard.js +351 -0
- package/dist/v2/components/PeriodCard/PeriodCard.scss.js +6 -0
- package/dist/v2/components/PeriodCard/index.d.ts +3 -0
- package/dist/v2/components/PeriodCard/index.d.ts.map +1 -0
- package/dist/v2/components/ReorderRow/ReorderRow.d.ts +24 -0
- package/dist/v2/components/ReorderRow/ReorderRow.d.ts.map +1 -0
- package/dist/v2/components/ReorderRow/ReorderRow.js +109 -0
- package/dist/v2/components/ReorderRow/ReorderRow.scss.js +6 -0
- package/dist/v2/components/ReorderRow/index.d.ts +3 -0
- package/dist/v2/components/ReorderRow/index.d.ts.map +1 -0
- package/dist/v2/components/index.d.ts +4 -0
- package/dist/v2/components/index.d.ts.map +1 -1
- package/dist/v2/index.d.ts +6 -0
- package/dist/v2/index.d.ts.map +1 -1
- package/dist/v2/pages/DeleteEntity/DeleteEntityPage.d.ts +29 -0
- package/dist/v2/pages/DeleteEntity/DeleteEntityPage.d.ts.map +1 -0
- package/dist/v2/pages/DeleteEntity/DeleteEntityPage.js +294 -0
- package/dist/v2/pages/DeleteEntity/DeleteEntityPage.scss.js +6 -0
- package/dist/v2/pages/DeleteEntity/index.d.ts +3 -0
- package/dist/v2/pages/DeleteEntity/index.d.ts.map +1 -0
- package/dist/v2/pages/Settings/components/SidebarCustomisation.js +2 -0
- package/dist/v2/pages/Settings/components/SidebarNavItem.js +2 -0
- package/dist/v2/pages/SettingsSubPage/SettingsSubPage.js +47 -0
- package/dist/v2/pages/SettingsSubPage/SettingsSubPage.scss.js +6 -0
- package/dist/v2/styles/components/Button.scss +27 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/v2/components/Button/Button.tsx +1 -0
- package/src/v2/components/PeriodCard/PeriodCard.scss +157 -0
- package/src/v2/components/PeriodCard/PeriodCard.stories.tsx +245 -0
- package/src/v2/components/PeriodCard/PeriodCard.tsx +350 -0
- package/src/v2/components/PeriodCard/index.ts +8 -0
- package/src/v2/components/ReorderRow/ReorderRow.scss +68 -0
- package/src/v2/components/ReorderRow/ReorderRow.stories.tsx +124 -0
- package/src/v2/components/ReorderRow/ReorderRow.tsx +88 -0
- package/src/v2/components/ReorderRow/index.ts +2 -0
- package/src/v2/components/index.ts +6 -0
- package/src/v2/index.ts +13 -0
- package/src/v2/pages/DeleteEntity/DeleteEntityPage.scss +96 -0
- package/src/v2/pages/DeleteEntity/DeleteEntityPage.stories.tsx +83 -0
- package/src/v2/pages/DeleteEntity/DeleteEntityPage.tsx +120 -0
- package/src/v2/pages/DeleteEntity/index.ts +2 -0
- package/src/v2/styles/components/Button.scss +27 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { NewInput } from '../NewInput'
|
|
3
|
+
import { Checkbox } from '../Checkbox'
|
|
4
|
+
import { IconButton } from '../IconButton'
|
|
5
|
+
import { ClockIcon } from '../../icons'
|
|
6
|
+
import './PeriodCard.scss'
|
|
7
|
+
|
|
8
|
+
export interface PeriodCardValue {
|
|
9
|
+
days: string[]
|
|
10
|
+
is24Hours: boolean
|
|
11
|
+
startTime?: string
|
|
12
|
+
endTime?: string
|
|
13
|
+
maxCapacity: number
|
|
14
|
+
minCapacity: number | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PeriodCardLabels {
|
|
18
|
+
badge?: string
|
|
19
|
+
sectionTitle?: string
|
|
20
|
+
minValue?: string
|
|
21
|
+
maxValue?: string
|
|
22
|
+
days?: string
|
|
23
|
+
time?: string
|
|
24
|
+
alignToggle?: string
|
|
25
|
+
startTime?: string
|
|
26
|
+
endTime?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DEFAULT_LABELS: Required<PeriodCardLabels> = {
|
|
30
|
+
badge: 'Period',
|
|
31
|
+
sectionTitle: 'Override',
|
|
32
|
+
minValue: 'Min',
|
|
33
|
+
maxValue: 'Max',
|
|
34
|
+
days: 'Days',
|
|
35
|
+
time: 'Time',
|
|
36
|
+
alignToggle: 'Align with Opening Hours',
|
|
37
|
+
startTime: 'Start Time',
|
|
38
|
+
endTime: 'End Time',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DAY_OPTIONS = [
|
|
42
|
+
'Monday',
|
|
43
|
+
'Tuesday',
|
|
44
|
+
'Wednesday',
|
|
45
|
+
'Thursday',
|
|
46
|
+
'Friday',
|
|
47
|
+
'Saturday',
|
|
48
|
+
'Sunday',
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
export interface PeriodCardProps {
|
|
52
|
+
/** Controlled value for built-in sections. Not needed when using children. */
|
|
53
|
+
value?: PeriodCardValue
|
|
54
|
+
/** Called when built-in sections change. Not needed when using children. */
|
|
55
|
+
onChange?: (value: PeriodCardValue) => void
|
|
56
|
+
onDelete?: () => void
|
|
57
|
+
labels?: PeriodCardLabels
|
|
58
|
+
dayLabels?: string[]
|
|
59
|
+
/** Plain text title rendered at the top (e.g. "Period 1"). Mutually exclusive with badge header. */
|
|
60
|
+
title?: string
|
|
61
|
+
/** Hide the badge + delete header row (useful with title) */
|
|
62
|
+
hideHeader?: boolean
|
|
63
|
+
/** Hide the min/max capacity section */
|
|
64
|
+
hideCapacity?: boolean
|
|
65
|
+
/** Hide the days selection section */
|
|
66
|
+
hideDays?: boolean
|
|
67
|
+
/** Hide the time section */
|
|
68
|
+
hideTime?: boolean
|
|
69
|
+
/** Make the min value field optional (shows "(optional)" label) */
|
|
70
|
+
minOptional?: boolean
|
|
71
|
+
/** Custom icon for the badge. Defaults to ClockIcon */
|
|
72
|
+
badgeIcon?: React.ReactNode
|
|
73
|
+
/** Custom content. When provided, built-in sections (capacity, days, time) are not rendered. */
|
|
74
|
+
children?: React.ReactNode
|
|
75
|
+
className?: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const PeriodCard: React.FC<PeriodCardProps> = ({
|
|
79
|
+
value,
|
|
80
|
+
onChange,
|
|
81
|
+
onDelete,
|
|
82
|
+
labels: labelsProp,
|
|
83
|
+
dayLabels,
|
|
84
|
+
title,
|
|
85
|
+
hideHeader = false,
|
|
86
|
+
hideCapacity = false,
|
|
87
|
+
hideDays = false,
|
|
88
|
+
hideTime = false,
|
|
89
|
+
minOptional = true,
|
|
90
|
+
badgeIcon,
|
|
91
|
+
children,
|
|
92
|
+
className = '',
|
|
93
|
+
}) => {
|
|
94
|
+
const labels = { ...DEFAULT_LABELS, ...labelsProp }
|
|
95
|
+
const days = dayLabels || DAY_OPTIONS
|
|
96
|
+
const hasChildren = !!children
|
|
97
|
+
|
|
98
|
+
const updateField = (updates: Partial<PeriodCardValue>) => {
|
|
99
|
+
if (value && onChange) {
|
|
100
|
+
onChange({ ...value, ...updates })
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const toggleDay = (day: string) => {
|
|
105
|
+
if (!value) return
|
|
106
|
+
const current = value.days || []
|
|
107
|
+
const updated = current.includes(day)
|
|
108
|
+
? current.filter((d) => d !== day)
|
|
109
|
+
: [...current, day]
|
|
110
|
+
updateField({ days: updated })
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const toggleAlignHours = (checked: boolean) => {
|
|
114
|
+
if (checked) {
|
|
115
|
+
updateField({ is24Hours: true, startTime: undefined, endTime: undefined })
|
|
116
|
+
} else {
|
|
117
|
+
updateField({ is24Hours: false, startTime: '09:00', endTime: '17:00' })
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const leftDays = days.slice(0, 4)
|
|
122
|
+
const rightDays = days.slice(4)
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div className={`period-card ${className}`}>
|
|
126
|
+
{/* Title (plain text heading) */}
|
|
127
|
+
{title && (
|
|
128
|
+
<div className="period-card__title-row">
|
|
129
|
+
<span className="period-card__title">{title}</span>
|
|
130
|
+
{onDelete && <IconButton icon="delete" onClick={onDelete} />}
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
{/* Badge header */}
|
|
135
|
+
{!hideHeader && !title && (
|
|
136
|
+
<div className="period-card__header">
|
|
137
|
+
<div className="period-card__badge-group">
|
|
138
|
+
{badgeIcon || <ClockIcon width={20} height={20} />}
|
|
139
|
+
<span className="period-card__badge">{labels.badge}</span>
|
|
140
|
+
</div>
|
|
141
|
+
{onDelete && (
|
|
142
|
+
<IconButton icon="delete" onClick={onDelete} />
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{/* Custom children mode */}
|
|
148
|
+
{hasChildren ? (
|
|
149
|
+
children
|
|
150
|
+
) : (
|
|
151
|
+
<>
|
|
152
|
+
{/* Capacity / Value Override */}
|
|
153
|
+
{!hideCapacity && value && (
|
|
154
|
+
<div className="period-card__section">
|
|
155
|
+
<label className="period-card__section-label">
|
|
156
|
+
{labels.sectionTitle}
|
|
157
|
+
</label>
|
|
158
|
+
<div className="period-card__row">
|
|
159
|
+
<NewInput
|
|
160
|
+
label={labels.minValue}
|
|
161
|
+
optional={minOptional}
|
|
162
|
+
type="number"
|
|
163
|
+
min={0}
|
|
164
|
+
value={
|
|
165
|
+
value.minCapacity === null || value.minCapacity === 0
|
|
166
|
+
? ''
|
|
167
|
+
: value.minCapacity
|
|
168
|
+
}
|
|
169
|
+
onChange={(e) =>
|
|
170
|
+
updateField({
|
|
171
|
+
minCapacity:
|
|
172
|
+
(e.target as HTMLInputElement).value === ''
|
|
173
|
+
? null
|
|
174
|
+
: parseInt((e.target as HTMLInputElement).value, 10) || 0,
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
/>
|
|
178
|
+
<NewInput
|
|
179
|
+
label={labels.maxValue}
|
|
180
|
+
type="number"
|
|
181
|
+
min={1}
|
|
182
|
+
value={value.maxCapacity === 0 ? '' : value.maxCapacity}
|
|
183
|
+
onChange={(e) =>
|
|
184
|
+
updateField({
|
|
185
|
+
maxCapacity:
|
|
186
|
+
(e.target as HTMLInputElement).value === ''
|
|
187
|
+
? 0
|
|
188
|
+
: parseInt((e.target as HTMLInputElement).value, 10) || 0,
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
/>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
)}
|
|
195
|
+
|
|
196
|
+
{/* Days */}
|
|
197
|
+
{!hideDays && value && (
|
|
198
|
+
<div className="period-card__section">
|
|
199
|
+
<label className="period-card__section-label">
|
|
200
|
+
{labels.days}
|
|
201
|
+
</label>
|
|
202
|
+
<div className="period-card__days-grid">
|
|
203
|
+
<div className="period-card__days-col">
|
|
204
|
+
{leftDays.map((day) => (
|
|
205
|
+
<DayCheckbox
|
|
206
|
+
key={day}
|
|
207
|
+
label={day}
|
|
208
|
+
checked={value.days?.includes(day) || false}
|
|
209
|
+
onChange={() => toggleDay(day)}
|
|
210
|
+
/>
|
|
211
|
+
))}
|
|
212
|
+
</div>
|
|
213
|
+
<div className="period-card__days-col">
|
|
214
|
+
{rightDays.map((day) => (
|
|
215
|
+
<DayCheckbox
|
|
216
|
+
key={day}
|
|
217
|
+
label={day}
|
|
218
|
+
checked={value.days?.includes(day) || false}
|
|
219
|
+
onChange={() => toggleDay(day)}
|
|
220
|
+
/>
|
|
221
|
+
))}
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
)}
|
|
226
|
+
|
|
227
|
+
{/* Time */}
|
|
228
|
+
{!hideTime && value && (
|
|
229
|
+
<div className="period-card__section">
|
|
230
|
+
<label className="period-card__section-label">
|
|
231
|
+
{labels.time}
|
|
232
|
+
</label>
|
|
233
|
+
<Checkbox
|
|
234
|
+
label={labels.alignToggle}
|
|
235
|
+
checked={value.is24Hours ?? true}
|
|
236
|
+
onChange={(e) => toggleAlignHours(e.target.checked)}
|
|
237
|
+
/>
|
|
238
|
+
{!value.is24Hours && (
|
|
239
|
+
<div className="period-card__row period-card__row--time">
|
|
240
|
+
<NewInput
|
|
241
|
+
label={labels.startTime}
|
|
242
|
+
type="time"
|
|
243
|
+
value={value.startTime || '09:00'}
|
|
244
|
+
onChange={(e) =>
|
|
245
|
+
updateField({ startTime: (e.target as HTMLInputElement).value })
|
|
246
|
+
}
|
|
247
|
+
/>
|
|
248
|
+
<NewInput
|
|
249
|
+
label={labels.endTime}
|
|
250
|
+
type="time"
|
|
251
|
+
value={value.endTime || '17:00'}
|
|
252
|
+
onChange={(e) =>
|
|
253
|
+
updateField({ endTime: (e.target as HTMLInputElement).value })
|
|
254
|
+
}
|
|
255
|
+
/>
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
)}
|
|
260
|
+
</>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ── Sub-components for composition ──
|
|
267
|
+
|
|
268
|
+
export interface PeriodCardSectionProps {
|
|
269
|
+
label?: string
|
|
270
|
+
children: React.ReactNode
|
|
271
|
+
className?: string
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export const PeriodCardSection: React.FC<PeriodCardSectionProps> = ({
|
|
275
|
+
label,
|
|
276
|
+
children,
|
|
277
|
+
className = '',
|
|
278
|
+
}) => (
|
|
279
|
+
<div className={`period-card__section ${className}`}>
|
|
280
|
+
{label && (
|
|
281
|
+
<label className="period-card__section-label">{label}</label>
|
|
282
|
+
)}
|
|
283
|
+
{children}
|
|
284
|
+
</div>
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
PeriodCardSection.displayName = 'PeriodCardSection'
|
|
288
|
+
|
|
289
|
+
export interface PeriodCardTimeRangeProps {
|
|
290
|
+
startTime: string
|
|
291
|
+
endTime: string
|
|
292
|
+
onStartTimeChange: (value: string) => void
|
|
293
|
+
onEndTimeChange: (value: string) => void
|
|
294
|
+
startLabel?: string
|
|
295
|
+
endLabel?: string
|
|
296
|
+
separator?: string
|
|
297
|
+
className?: string
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export const PeriodCardTimeRange: React.FC<PeriodCardTimeRangeProps> = ({
|
|
301
|
+
startTime,
|
|
302
|
+
endTime,
|
|
303
|
+
onStartTimeChange,
|
|
304
|
+
onEndTimeChange,
|
|
305
|
+
startLabel,
|
|
306
|
+
endLabel,
|
|
307
|
+
separator = 'to',
|
|
308
|
+
className = '',
|
|
309
|
+
}) => (
|
|
310
|
+
<div className={`period-card__time-range ${className}`}>
|
|
311
|
+
<NewInput
|
|
312
|
+
label={startLabel}
|
|
313
|
+
type="time"
|
|
314
|
+
value={startTime}
|
|
315
|
+
onChange={(e) => onStartTimeChange((e.target as HTMLInputElement).value)}
|
|
316
|
+
/>
|
|
317
|
+
<span className="period-card__time-separator">{separator}</span>
|
|
318
|
+
<NewInput
|
|
319
|
+
label={endLabel}
|
|
320
|
+
type="time"
|
|
321
|
+
value={endTime}
|
|
322
|
+
onChange={(e) => onEndTimeChange((e.target as HTMLInputElement).value)}
|
|
323
|
+
/>
|
|
324
|
+
</div>
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
PeriodCardTimeRange.displayName = 'PeriodCardTimeRange'
|
|
328
|
+
|
|
329
|
+
// ── Day checkbox (also exported for standalone use) ──
|
|
330
|
+
|
|
331
|
+
interface DayCheckboxProps {
|
|
332
|
+
label: string
|
|
333
|
+
checked: boolean
|
|
334
|
+
onChange: () => void
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const DayCheckbox: React.FC<DayCheckboxProps> = ({ label, checked, onChange }) => (
|
|
338
|
+
<label className="day-checkbox">
|
|
339
|
+
<input
|
|
340
|
+
type="checkbox"
|
|
341
|
+
className="day-checkbox__input"
|
|
342
|
+
checked={checked}
|
|
343
|
+
onChange={onChange}
|
|
344
|
+
/>
|
|
345
|
+
<span className="day-checkbox__box" />
|
|
346
|
+
<span className="day-checkbox__label">{label}</span>
|
|
347
|
+
</label>
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
PeriodCard.displayName = 'PeriodCard'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
.reorder-row {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: 12px;
|
|
5
|
+
padding: 12px 16px;
|
|
6
|
+
background: var(--surface-secondary, #f5f6fa);
|
|
7
|
+
border-radius: 8px;
|
|
8
|
+
|
|
9
|
+
&--clickable {
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
|
|
12
|
+
&:hover {
|
|
13
|
+
background: var(--surface-secondary-hover, #edeef3);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&__drag-handle {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
cursor: grab;
|
|
21
|
+
color: var(--label-secondary, #6b7a99);
|
|
22
|
+
flex-shrink: 0;
|
|
23
|
+
|
|
24
|
+
&:active {
|
|
25
|
+
cursor: grabbing;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&__left {
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&__content {
|
|
36
|
+
flex: 1;
|
|
37
|
+
min-width: 0;
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&__label {
|
|
43
|
+
font-family: var(--font-family-sans, 'Geist', system-ui, sans-serif);
|
|
44
|
+
font-size: 14px;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
line-height: 20px;
|
|
47
|
+
color: var(--label-primary, #121e52);
|
|
48
|
+
white-space: nowrap;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
text-overflow: ellipsis;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&__subtitle {
|
|
54
|
+
font-family: var(--font-family-sans, 'Geist', system-ui, sans-serif);
|
|
55
|
+
font-size: 13px;
|
|
56
|
+
font-weight: 400;
|
|
57
|
+
line-height: 16px;
|
|
58
|
+
color: var(--label-secondary, #6b7a99);
|
|
59
|
+
margin-top: 2px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&__right {
|
|
63
|
+
flex-shrink: 0;
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 8px;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
3
|
+
import { ReorderRow } from './ReorderRow'
|
|
4
|
+
import { Badge } from '../Badge'
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: 'v2/Components/ReorderRow',
|
|
8
|
+
component: ReorderRow,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'padded',
|
|
11
|
+
},
|
|
12
|
+
} as Meta<typeof ReorderRow>
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof ReorderRow>
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
label: 'Test Zone',
|
|
19
|
+
subtitle: '0 resources · Total capacity: 0',
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const WithSubtitle: Story = {
|
|
24
|
+
args: {
|
|
25
|
+
label: 'Bowling Alley',
|
|
26
|
+
subtitle: '3 resources · Total capacity: 12',
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const WithoutSubtitle: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
label: 'VIP Lounge',
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const NoDragHandle: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
label: 'Static Row',
|
|
39
|
+
subtitle: 'No drag handle shown',
|
|
40
|
+
hideDragHandle: true,
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Clickable: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
label: 'Click me',
|
|
47
|
+
subtitle: 'Has hover state',
|
|
48
|
+
onClick: () => alert('Clicked!'),
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const MultipleRows: Story = {
|
|
53
|
+
render: () => (
|
|
54
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', maxWidth: 500 }}>
|
|
55
|
+
<ReorderRow
|
|
56
|
+
label="Main Floor"
|
|
57
|
+
subtitle="5 resources · Total capacity: 20"
|
|
58
|
+
/>
|
|
59
|
+
<ReorderRow
|
|
60
|
+
label="VIP Lounge"
|
|
61
|
+
subtitle="2 resources · Total capacity: 8"
|
|
62
|
+
/>
|
|
63
|
+
<ReorderRow
|
|
64
|
+
label="Outdoor Area"
|
|
65
|
+
subtitle="3 resources · Total capacity: 15"
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const ColorDot = ({ color }: { color: string }) => (
|
|
72
|
+
<span
|
|
73
|
+
style={{
|
|
74
|
+
width: 12,
|
|
75
|
+
height: 12,
|
|
76
|
+
borderRadius: '50%',
|
|
77
|
+
backgroundColor: color,
|
|
78
|
+
display: 'inline-block',
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
export const PriorityReorder: Story = {
|
|
84
|
+
render: () => (
|
|
85
|
+
<div style={{ maxWidth: 500 }}>
|
|
86
|
+
<div style={{ marginBottom: 16 }}>
|
|
87
|
+
<h3 style={{ margin: '0 0 4px', fontSize: 16, fontWeight: 600, color: '#121e52' }}>
|
|
88
|
+
Set a new priority order for your pricing periods
|
|
89
|
+
</h3>
|
|
90
|
+
<p style={{ margin: 0, fontSize: 13, color: '#6b7a99' }}>
|
|
91
|
+
Update the priority order of your pricing periods. The first period in the list has the highest priority and will take precedence when multiple periods overlap.
|
|
92
|
+
</p>
|
|
93
|
+
</div>
|
|
94
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
|
95
|
+
<ReorderRow
|
|
96
|
+
label="Peak"
|
|
97
|
+
subtitle="Peak Period"
|
|
98
|
+
leftContent={<ColorDot color="#ef4444" />}
|
|
99
|
+
rightContent={<Badge variant="neutral">Priority 1</Badge>}
|
|
100
|
+
/>
|
|
101
|
+
<ReorderRow
|
|
102
|
+
label="Xmas"
|
|
103
|
+
leftContent={<ColorDot color="#f97316" />}
|
|
104
|
+
rightContent={<Badge variant="neutral">Priority 2</Badge>}
|
|
105
|
+
/>
|
|
106
|
+
<ReorderRow
|
|
107
|
+
label="Off Peak"
|
|
108
|
+
subtitle="This is for when we're not busy."
|
|
109
|
+
leftContent={<ColorDot color="#3b82f6" />}
|
|
110
|
+
rightContent={<Badge variant="neutral">Priority 3</Badge>}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const WithLeftAndRightContent: Story = {
|
|
118
|
+
args: {
|
|
119
|
+
label: 'Peak',
|
|
120
|
+
subtitle: 'Peak Period',
|
|
121
|
+
leftContent: <ColorDot color="#ef4444" />,
|
|
122
|
+
rightContent: <Badge variant="neutral">Priority 1</Badge>,
|
|
123
|
+
},
|
|
124
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React, { HTMLAttributes } from 'react'
|
|
2
|
+
import { GripVerticalIcon } from '../../icons'
|
|
3
|
+
import './ReorderRow.scss'
|
|
4
|
+
|
|
5
|
+
export interface DragHandleRenderProps {
|
|
6
|
+
/** Spread onto the drag handle element for dnd-kit or similar */
|
|
7
|
+
attributes?: HTMLAttributes<HTMLElement>
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
9
|
+
listeners?: Record<string, Function> | undefined
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ReorderRowProps {
|
|
13
|
+
label: string
|
|
14
|
+
subtitle?: string
|
|
15
|
+
/** Render prop for the drag handle — receives attributes/listeners to spread */
|
|
16
|
+
dragHandleProps?: DragHandleRenderProps
|
|
17
|
+
/** Hide the drag handle (e.g. when not in reorder mode) */
|
|
18
|
+
hideDragHandle?: boolean
|
|
19
|
+
/** Content between the drag handle and label (e.g. color dot, icon) */
|
|
20
|
+
leftContent?: React.ReactNode
|
|
21
|
+
/** Right-side content (e.g. action menu, capacity badge) */
|
|
22
|
+
rightContent?: React.ReactNode
|
|
23
|
+
onClick?: () => void
|
|
24
|
+
className?: string
|
|
25
|
+
style?: React.CSSProperties
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const ReorderRow = React.forwardRef<HTMLDivElement, ReorderRowProps>(
|
|
29
|
+
(
|
|
30
|
+
{
|
|
31
|
+
label,
|
|
32
|
+
subtitle,
|
|
33
|
+
dragHandleProps,
|
|
34
|
+
hideDragHandle = false,
|
|
35
|
+
leftContent,
|
|
36
|
+
rightContent,
|
|
37
|
+
onClick,
|
|
38
|
+
className = '',
|
|
39
|
+
style,
|
|
40
|
+
},
|
|
41
|
+
ref,
|
|
42
|
+
) => {
|
|
43
|
+
const handleKeyDown = onClick
|
|
44
|
+
? (e: React.KeyboardEvent) => {
|
|
45
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
46
|
+
e.preventDefault()
|
|
47
|
+
onClick()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
: undefined
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
ref={ref}
|
|
55
|
+
style={style}
|
|
56
|
+
className={`reorder-row ${onClick ? 'reorder-row--clickable' : ''} ${className}`}
|
|
57
|
+
onClick={onClick}
|
|
58
|
+
onKeyDown={handleKeyDown}
|
|
59
|
+
role={onClick ? 'button' : undefined}
|
|
60
|
+
tabIndex={onClick ? 0 : undefined}
|
|
61
|
+
>
|
|
62
|
+
{!hideDragHandle && (
|
|
63
|
+
<div
|
|
64
|
+
className="reorder-row__drag-handle"
|
|
65
|
+
{...dragHandleProps?.attributes}
|
|
66
|
+
{...dragHandleProps?.listeners}
|
|
67
|
+
>
|
|
68
|
+
{GripVerticalIcon({ width: 20, height: 20 })}
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
{leftContent && (
|
|
72
|
+
<div className="reorder-row__left">{leftContent}</div>
|
|
73
|
+
)}
|
|
74
|
+
<div className="reorder-row__content">
|
|
75
|
+
<span className="reorder-row__label">{label}</span>
|
|
76
|
+
{subtitle && (
|
|
77
|
+
<span className="reorder-row__subtitle">{subtitle}</span>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
{rightContent && (
|
|
81
|
+
<div className="reorder-row__right">{rightContent}</div>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
)
|
|
85
|
+
},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
ReorderRow.displayName = 'ReorderRow'
|
|
@@ -68,6 +68,12 @@ export type { PaginationProps } from './Pagination'
|
|
|
68
68
|
export { ZoneCard, ZoneContainer, ZoneHeader, ResourceRow, AddResourceButton } from './ZoneCard'
|
|
69
69
|
export type { ZoneCardProps, ZoneResource, ZoneContainerProps, DragHandleProps, ZoneHeaderProps, ResourceRowProps, AddResourceButtonProps } from './ZoneCard'
|
|
70
70
|
|
|
71
|
+
export { PeriodCard, PeriodCardSection, PeriodCardTimeRange } from './PeriodCard'
|
|
72
|
+
|
|
73
|
+
export { ReorderRow } from './ReorderRow'
|
|
74
|
+
export type { ReorderRowProps, DragHandleRenderProps } from './ReorderRow'
|
|
75
|
+
export type { PeriodCardProps, PeriodCardValue, PeriodCardLabels, PeriodCardSectionProps, PeriodCardTimeRangeProps } from './PeriodCard'
|
|
76
|
+
|
|
71
77
|
// Icons
|
|
72
78
|
export {
|
|
73
79
|
InfoIcon,
|
package/src/v2/index.ts
CHANGED
|
@@ -53,6 +53,19 @@ export type { UserPanelProps } from './components/UserPanel'
|
|
|
53
53
|
export { ZoneCard, ZoneContainer, ZoneHeader, ResourceRow, AddResourceButton } from './components/ZoneCard'
|
|
54
54
|
export type { ZoneCardProps, ZoneResource, ZoneContainerProps, DragHandleProps, ZoneHeaderProps, ResourceRowProps, ResourceRowLabels, AddResourceButtonProps } from './components/ZoneCard'
|
|
55
55
|
|
|
56
|
+
// SettingsSubPage (generic settings sub-page layout)
|
|
57
|
+
export { SettingsSubPage } from './pages/SettingsSubPage'
|
|
58
|
+
export type { SettingsSubPageProps } from './pages/SettingsSubPage'
|
|
59
|
+
|
|
60
|
+
// ZonesResources Page (deprecated — use SettingsSubPage)
|
|
61
|
+
// PeriodCard Component
|
|
62
|
+
export { PeriodCard, PeriodCardSection, PeriodCardTimeRange } from './components/PeriodCard'
|
|
63
|
+
export type { PeriodCardProps, PeriodCardValue, PeriodCardLabels, PeriodCardSectionProps, PeriodCardTimeRangeProps } from './components/PeriodCard'
|
|
64
|
+
|
|
65
|
+
// ReorderRow Component
|
|
66
|
+
export { ReorderRow } from './components/ReorderRow'
|
|
67
|
+
export type { ReorderRowProps, DragHandleRenderProps } from './components/ReorderRow'
|
|
68
|
+
|
|
56
69
|
// ZonesResources Page
|
|
57
70
|
export { ZonesResourcesPage } from './pages/ZonesResources'
|
|
58
71
|
export type { ZonesResourcesPageProps } from './pages/ZonesResources'
|