@qiiqa/bob-ui-react-layout 0.2.0

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.
@@ -0,0 +1,21 @@
1
+
2
+ > @qiiqa/bob-ui-react-layout@0.1.0 build F:\Dev\ai-bob\packages\qiiqa\bob-ui-react-layout
3
+ > tsup src/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.json
4
+
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.1
8
+ CLI Target: esnext
9
+ CLI Cleaning output folder
10
+ CJS Build start
11
+ ESM Build start
12
+ ESM dist\index.css 9.75 KB
13
+ ESM dist\index.mjs 42.82 KB
14
+ ESM ⚡️ Build success in 49ms
15
+ CJS dist\index.css 9.75 KB
16
+ CJS dist\index.js 47.27 KB
17
+ CJS ⚡️ Build success in 49ms
18
+ DTS Build start
19
+ DTS ⚡️ Build success in 1967ms
20
+ DTS dist\index.d.ts 6.21 KB
21
+ DTS dist\index.d.mts 6.21 KB
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @qiiqa/bob-ui-react-layout
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Initial basic functionality and ui theming
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @qiiqa/bob-ui-react-components@0.2.0
13
+ - @qiiqa/bob-ui@0.2.0
package/dist/index.css ADDED
@@ -0,0 +1,429 @@
1
+ /* src/css/bob-react-layout.css */
2
+ .bob-react-form {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 16px;
6
+ width: 100%;
7
+ min-height: 100%;
8
+ max-width: 100%;
9
+ overflow-x: hidden;
10
+ }
11
+ .bob-react-page {
12
+ display: grid;
13
+ gap: 16px;
14
+ align-content: start;
15
+ align-items: start;
16
+ width: 100%;
17
+ box-sizing: border-box;
18
+ }
19
+ .bob-react-panel-header {
20
+ margin: 0 0 16px 0;
21
+ grid-column: 1 / -1;
22
+ display: flex;
23
+ align-items: center;
24
+ min-height: 24px;
25
+ width: 100%;
26
+ }
27
+ .bob-react-panel-caption {
28
+ margin: 0;
29
+ font-size: 1.25rem;
30
+ font-weight: 600;
31
+ color: var(--q-text-base);
32
+ }
33
+ .bob-react-panel-design-controls {
34
+ display: flex;
35
+ align-items: center;
36
+ margin-right: 12px;
37
+ gap: 8px;
38
+ }
39
+ .bob-react-panel-drag-handle {
40
+ cursor: grab;
41
+ color: var(--q-icon-color);
42
+ }
43
+ .bob-react-panel-resize-controls {
44
+ display: flex;
45
+ gap: 4px;
46
+ align-items: center;
47
+ }
48
+ .bob-react-panel-resize-btn {
49
+ background: var(--q-bg-hover);
50
+ border: 1px solid var(--q-border-color);
51
+ border-radius: 4px;
52
+ width: 20px;
53
+ height: 20px;
54
+ cursor: pointer;
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ padding: 0;
59
+ }
60
+ .bob-react-panel-span-label {
61
+ font-size: 0.75rem;
62
+ color: var(--q-text-muted);
63
+ padding: 0 4px;
64
+ user-select: none;
65
+ }
66
+ .bob-react-panel-fields-row {
67
+ grid-column: 1 / -1;
68
+ display: grid;
69
+ grid-template-columns: repeat(var(--panel-columns, 12), 1fr);
70
+ width: 100%;
71
+ }
72
+ .bob-react-panel {
73
+ display: grid;
74
+ flex-grow: 1;
75
+ gap: 0;
76
+ align-content: start;
77
+ align-items: start;
78
+ position: relative;
79
+ padding: 8px;
80
+ border: 1px solid var(--q-border-color);
81
+ border-radius: var(--q-border-radius);
82
+ background: var(--q-bg-base);
83
+ box-shadow: var(--q-shadow-sm);
84
+ width: 100%;
85
+ min-width: 0;
86
+ }
87
+ .bob-react-panel-design-mode {
88
+ border: 2px solid var(--q-primary);
89
+ background-color: var(--q-primary-dark);
90
+ color: var(--q-text-on-dark);
91
+ padding-bottom: 40px;
92
+ }
93
+ .bob-react-panel-design-mode .bob-react-panel-caption {
94
+ color: var(--q-text-on-dark);
95
+ }
96
+ .bob-react-panel-row-controls {
97
+ position: absolute;
98
+ top: -12px;
99
+ left: 50%;
100
+ transform: translateX(-50%);
101
+ display: flex;
102
+ align-items: center;
103
+ z-index: 30;
104
+ background: var(--q-bg-base);
105
+ border: 1px solid var(--q-primary);
106
+ border-radius: 4px;
107
+ padding: 1px;
108
+ box-shadow: var(--q-shadow-sm);
109
+ }
110
+ .bob-react-item-row-controls {
111
+ position: absolute;
112
+ top: 4px;
113
+ right: 4px;
114
+ display: flex;
115
+ align-items: center;
116
+ z-index: 30;
117
+ background: var(--q-bg-base);
118
+ border: 1px solid var(--q-primary);
119
+ border-radius: 4px;
120
+ padding: 1px;
121
+ box-shadow: var(--q-shadow-sm);
122
+ }
123
+ .bob-react-panel-dragover {
124
+ background-color: var(--q-bg-hover) !important;
125
+ box-shadow: var(--q-shadow-primary-inset);
126
+ border-color: var(--q-primary) !important;
127
+ }
128
+ .bob-react-item {
129
+ display: flex !important;
130
+ flex-direction: column;
131
+ justify-content: flex-end;
132
+ gap: 4px;
133
+ position: relative;
134
+ width: 100%;
135
+ min-width: 0;
136
+ min-height: 0;
137
+ align-self: stretch;
138
+ padding: 8px 8px 8px 0;
139
+ }
140
+ .bob-react-item-expand-vertical {
141
+ align-self: stretch;
142
+ height: 100%;
143
+ }
144
+ .bob-react-item-caption {
145
+ font-size: var(--q-font-size-base);
146
+ font-weight: 500;
147
+ color: var(--q-text-base);
148
+ margin-bottom: 2px;
149
+ word-break: break-word;
150
+ }
151
+ .bob-react-item-horizontal {
152
+ display: grid;
153
+ align-items: center;
154
+ gap: 12px;
155
+ width: 100%;
156
+ }
157
+ .bob-react-item-horizontal-caption {
158
+ font-size: var(--q-font-size-base);
159
+ font-weight: 500;
160
+ color: var(--q-text-base);
161
+ word-break: break-word;
162
+ }
163
+ .bob-react-item-horizontal-content {
164
+ min-width: 0;
165
+ }
166
+ .bob-react-item-row-btn {
167
+ background: transparent;
168
+ border: none;
169
+ cursor: pointer;
170
+ padding: 0 4px;
171
+ font-size: 12px;
172
+ font-weight: bold;
173
+ color: var(--q-primary);
174
+ }
175
+ .bob-react-gap-zone {
176
+ position: relative;
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: center;
180
+ width: 20px;
181
+ min-height: 100%;
182
+ background: transparent;
183
+ transition: background 0.15s ease;
184
+ flex-shrink: 0;
185
+ grid-column: unset !important;
186
+ }
187
+ .bob-react-gap-zone.start {
188
+ border-radius: 4px 0 0 4px;
189
+ }
190
+ .bob-react-gap-zone.end {
191
+ border-radius: 0 4px 4px 0;
192
+ }
193
+ .bob-react-gap-zone.bob-react-gap-zone-active {
194
+ background: var(--q-primary-alpha-20);
195
+ box-shadow: inset 0 0 0 2px var(--q-primary);
196
+ border-radius: 4px;
197
+ }
198
+ .bob-react-gap-controls {
199
+ display: flex;
200
+ flex-direction: column;
201
+ align-items: center;
202
+ gap: 0;
203
+ background: var(--q-bg-base);
204
+ border: 1px solid var(--q-primary);
205
+ border-radius: 4px;
206
+ padding: 1px;
207
+ box-shadow: var(--q-shadow-sm);
208
+ z-index: 20;
209
+ pointer-events: all;
210
+ }
211
+ .bob-react-gap-btn {
212
+ background: transparent;
213
+ color: var(--q-primary);
214
+ border: none;
215
+ width: 14px;
216
+ height: 14px;
217
+ cursor: pointer;
218
+ display: flex;
219
+ align-items: center;
220
+ justify-content: center;
221
+ padding: 0;
222
+ font-size: 11px;
223
+ font-weight: bold;
224
+ border-radius: 2px;
225
+ transition: background 0.1s;
226
+ }
227
+ .bob-react-gap-btn:hover {
228
+ background: var(--q-bg-hover);
229
+ }
230
+ .bob-react-gap-span-label {
231
+ font-size: 0.55rem;
232
+ color: var(--q-text-muted);
233
+ padding: 0;
234
+ user-select: none;
235
+ text-align: center;
236
+ font-weight: 700;
237
+ min-width: 12px;
238
+ line-height: 1;
239
+ }
240
+ .bob-react-item-selected .bob-react-overlay {
241
+ background-color: var(--q-primary-alpha-15);
242
+ border: 2px solid var(--q-primary);
243
+ }
244
+ .bob-react-item-selected .bob-react-overlay::after {
245
+ content: "SELECTED";
246
+ background: var(--q-primary-dark);
247
+ }
248
+ .bob-react-tab-strip {
249
+ display: flex;
250
+ gap: 8px;
251
+ border-bottom: 1px solid var(--q-border-color);
252
+ margin-bottom: 16px;
253
+ margin-left: 3px;
254
+ }
255
+ .bob-react-tab {
256
+ padding: 8px 16px;
257
+ border: 1px solid transparent;
258
+ border-bottom: none;
259
+ cursor: pointer;
260
+ font-weight: 500;
261
+ color: var(--q-text-muted);
262
+ transition: all 0.2s;
263
+ margin-bottom: -1px;
264
+ border-radius: 6px 6px 0 0;
265
+ background: transparent;
266
+ }
267
+ .bob-react-tab:hover {
268
+ color: var(--q-primary);
269
+ background: var(--q-bg-hover);
270
+ }
271
+ .bob-react-tab-active {
272
+ background: var(--q-bg-base);
273
+ color: var(--q-primary);
274
+ border-color: var(--q-border-color);
275
+ border-bottom: 2px solid var(--q-primary);
276
+ z-index: 10;
277
+ }
278
+ .theme-variant-clean .bob-react-tab-strip {
279
+ border-bottom-color: transparent;
280
+ }
281
+ .theme-variant-clean .bob-react-tab {
282
+ border-color: transparent !important;
283
+ }
284
+ .theme-variant-clean .bob-react-tab-active {
285
+ border-color: transparent !important;
286
+ border-bottom-color: var(--q-primary) !important;
287
+ }
288
+ .bob-react-overlay {
289
+ position: absolute;
290
+ inset: 4px;
291
+ background-color: var(--q-overlay-bg);
292
+ z-index: 10;
293
+ cursor: move;
294
+ border-radius: 4px;
295
+ border: 1px dashed var(--q-primary);
296
+ display: flex;
297
+ align-items: center;
298
+ justify-content: center;
299
+ }
300
+ .bob-react-overlay::after {
301
+ content: "DRAG";
302
+ font-size: 0.75rem;
303
+ color: var(--q-bg-base);
304
+ background: var(--q-primary);
305
+ padding: 2px 6px;
306
+ border-radius: 4px;
307
+ }
308
+ .bob-react-drop-indicator {
309
+ position: absolute;
310
+ background: var(--q-primary);
311
+ z-index: 50;
312
+ pointer-events: none;
313
+ }
314
+ .bob-react-drop-indicator.left {
315
+ left: 0px;
316
+ top: 0;
317
+ bottom: 0;
318
+ width: 4px;
319
+ border-radius: 2px;
320
+ }
321
+ .bob-react-drop-indicator.right {
322
+ right: 0px;
323
+ top: 0;
324
+ bottom: 0;
325
+ width: 4px;
326
+ border-radius: 2px;
327
+ }
328
+ .bob-react-drop-indicator.top {
329
+ top: 0px;
330
+ left: 0;
331
+ right: 0;
332
+ height: 4px;
333
+ border-radius: 2px;
334
+ }
335
+ .bob-react-drop-indicator.bottom {
336
+ bottom: 0px;
337
+ left: 0;
338
+ right: 0;
339
+ height: 4px;
340
+ border-radius: 2px;
341
+ }
342
+ .bob-react-panel-compact {
343
+ padding: 8px !important;
344
+ gap: 4px !important;
345
+ }
346
+ .bob-react-panel-compact .bob-react-panel-header {
347
+ margin-bottom: 8px !important;
348
+ }
349
+ .bob-react-panel-compact .bob-react-panel-caption {
350
+ font-size: 0.9rem !important;
351
+ }
352
+ .bob-react-panel-compact .bob-react-item {
353
+ padding: 4px 4px 4px 0 !important;
354
+ }
355
+ .bob-react-responsiveness {
356
+ position: relative;
357
+ display: flex;
358
+ align-items: center;
359
+ }
360
+ .bob-react-responsiveness-display {
361
+ display: flex;
362
+ align-items: center;
363
+ gap: 4px;
364
+ cursor: pointer;
365
+ font-size: 12px;
366
+ padding: 4px 8px;
367
+ border-radius: 4px;
368
+ border: 1px solid var(--q-border-color);
369
+ background: var(--q-bg-base);
370
+ color: var(--q-text-base);
371
+ }
372
+ .bob-react-responsiveness-display span:first-child {
373
+ font-weight: 600;
374
+ }
375
+ .bob-react-responsiveness-display span:nth-child(2) {
376
+ opacity: 0.6;
377
+ }
378
+ .bob-react-responsiveness-display i {
379
+ font-size: 10px;
380
+ margin-left: 2px;
381
+ }
382
+ .bob-react-responsiveness-popup {
383
+ position: absolute;
384
+ top: calc(100% + 8px);
385
+ right: 0;
386
+ z-index: 1000;
387
+ background-color: var(--q-bg-sunken);
388
+ border: 1px solid var(--q-border-color);
389
+ border-radius: 12px;
390
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
391
+ padding: 12px;
392
+ width: 280px;
393
+ }
394
+ .bob-react-responsiveness-popup-header {
395
+ display: flex;
396
+ justify-content: space-between;
397
+ align-items: center;
398
+ margin-bottom: 16px;
399
+ }
400
+ .bob-react-responsiveness-popup-header span {
401
+ font-weight: 600;
402
+ font-size: 16px;
403
+ color: var(--q-text-base);
404
+ }
405
+ .bob-react-responsiveness-popup-close-btn {
406
+ background: none;
407
+ border: none;
408
+ cursor: pointer;
409
+ padding: 4px;
410
+ color: var(--q-text-muted);
411
+ font-size: 18px;
412
+ }
413
+ .bob-react-responsiveness-panel {
414
+ border: 1px solid var(--q-border-color);
415
+ background: var(--q-bg-base);
416
+ box-shadow: none;
417
+ margin-bottom: 4px;
418
+ }
419
+ .bob-react-responsiveness-panel.resp-panel-active {
420
+ }
421
+ .bob-react-responsiveness-input {
422
+ width: 100%;
423
+ padding: 2px 4px;
424
+ font-size: 11px;
425
+ border: 1px solid var(--q-border-color);
426
+ border-radius: 4px;
427
+ background-color: var(--q-bg-sunken);
428
+ color: var(--q-primary);
429
+ }
@@ -0,0 +1,185 @@
1
+ import React, { ReactNode } from 'react';
2
+
3
+ interface BobResponsivenessConfig {
4
+ small: {
5
+ page: number;
6
+ group: number;
7
+ };
8
+ medium: {
9
+ page: number;
10
+ group: number;
11
+ };
12
+ large: {
13
+ page: number;
14
+ group: number;
15
+ };
16
+ extraLarge: {
17
+ page: number;
18
+ group: number;
19
+ };
20
+ }
21
+ declare const DEFAULT_RESPONSIVENESS_CONFIG: BobResponsivenessConfig;
22
+ type LayoutStateDictionary = Record<string, string[]>;
23
+ interface LayoutContextState {
24
+ designMode: boolean;
25
+ designTabs: boolean;
26
+ designGroups: boolean;
27
+ designFields: boolean;
28
+ layoutState: LayoutStateDictionary;
29
+ selectedItemIds: string[];
30
+ isDraggingActive: boolean;
31
+ dragOverId: string | null;
32
+ dragSide: 'left' | 'right' | 'top' | 'bottom' | null;
33
+ pageCols: number;
34
+ groupCols: number;
35
+ setLayoutState: React.Dispatch<React.SetStateAction<LayoutStateDictionary>>;
36
+ toggleItemSelection: (id: string, multi: boolean) => void;
37
+ clearItemSelection: () => void;
38
+ setDraggingActive: (active: boolean) => void;
39
+ setDragOverState: (id: string | null, side: 'left' | 'right' | 'top' | 'bottom' | null) => void;
40
+ loadLayoutState: (state: LayoutStateDictionary) => void;
41
+ reorderItemWithinContainer: (containerId: string, activeId: string, overId: string, fallbackItems?: string[], insertAfter?: boolean) => void;
42
+ moveItemBetweenContainers: (sourceContainerId: string, destContainerId: string, activeId: string, overId: string, fallbackItems?: string[], insertAfter?: boolean) => void;
43
+ setDesignMode: (mode: boolean) => void;
44
+ setDesignTabs: (mode: boolean) => void;
45
+ setDesignGroups: (mode: boolean) => void;
46
+ setDesignFields: (mode: boolean) => void;
47
+ setPageCols: (cols: number) => void;
48
+ setGroupCols: (cols: number) => void;
49
+ responsivenessConfig: BobResponsivenessConfig;
50
+ setResponsivenessConfig: (config: BobResponsivenessConfig) => void;
51
+ currentBreakpoint: string;
52
+ }
53
+ declare function useLayoutContext(): LayoutContextState;
54
+ declare const LayoutProvider: React.FC<{
55
+ children: ReactNode;
56
+ initialDesignMode?: boolean;
57
+ initialDesignTabs?: boolean;
58
+ initialDesignGroups?: boolean;
59
+ initialDesignFields?: boolean;
60
+ initialLayoutState?: LayoutStateDictionary;
61
+ initialPageCols?: number;
62
+ initialGroupCols?: number;
63
+ initialResponsivenessConfig?: BobResponsivenessConfig;
64
+ onLayoutChange?: (state: LayoutStateDictionary) => void;
65
+ onPageColsChange?: (cols: number) => void;
66
+ onGroupColsChange?: (cols: number) => void;
67
+ onResponsivenessConfigChange?: (config: BobResponsivenessConfig) => void;
68
+ }>;
69
+
70
+ interface BobFormProps {
71
+ children: ReactNode;
72
+ className?: string;
73
+ designMode?: boolean;
74
+ designTabs?: boolean;
75
+ designGroups?: boolean;
76
+ designFields?: boolean;
77
+ layoutState?: Record<string, string[]>;
78
+ pageCols?: number;
79
+ groupCols?: number;
80
+ initialResponsivenessConfig?: any;
81
+ onLayoutChange?: (state: any) => void;
82
+ onPageColsChange?: (cols: number) => void;
83
+ onGroupColsChange?: (cols: number) => void;
84
+ onResponsivenessConfigChange?: (config: any) => void;
85
+ }
86
+ declare const BobForm: React.FC<BobFormProps>;
87
+
88
+ interface BobTabStripProps {
89
+ id: string;
90
+ children?: ReactNode;
91
+ className?: string;
92
+ }
93
+ declare const BobTabStrip: React.FC<BobTabStripProps>;
94
+
95
+ interface BobTabProps {
96
+ id: string;
97
+ label: string | ReactNode;
98
+ isActive?: boolean;
99
+ onClick?: (id: string) => void;
100
+ className?: string;
101
+ }
102
+ declare const BobTab: React.FC<BobTabProps>;
103
+
104
+ interface BobPageProps {
105
+ id: string;
106
+ children?: ReactNode;
107
+ columns?: number;
108
+ className?: string;
109
+ }
110
+ declare const BobPage: React.FC<BobPageProps>;
111
+
112
+ interface BobTabPageProps {
113
+ id?: string;
114
+ children?: ReactNode;
115
+ isActive?: boolean;
116
+ columns?: number;
117
+ className?: string;
118
+ pageClassName?: string;
119
+ }
120
+ declare const BobTabPage: React.FC<BobTabPageProps>;
121
+
122
+ interface BobPanelProps {
123
+ id: string;
124
+ children?: ReactNode;
125
+ colSpan?: number;
126
+ rowSpan?: number;
127
+ columns?: number;
128
+ className?: string;
129
+ caption?: string;
130
+ newline?: boolean;
131
+ onRowResize?: (delta: number) => void;
132
+ style?: React.CSSProperties;
133
+ }
134
+ declare const BobPanel: React.FC<BobPanelProps>;
135
+
136
+ interface BobItemProps {
137
+ id: string;
138
+ children?: ReactNode;
139
+ colSpan?: number;
140
+ rowSpan?: number;
141
+ className?: string;
142
+ expandVertical?: boolean;
143
+ caption?: string;
144
+ hideCaption?: boolean;
145
+ newline?: boolean;
146
+ onRowResize?: (delta: number) => void;
147
+ style?: React.CSSProperties;
148
+ horizontal?: boolean;
149
+ labelWidth?: string;
150
+ }
151
+ declare const BobItem: React.FC<BobItemProps>;
152
+
153
+ interface BobResponsivenessProps {
154
+ className?: string;
155
+ }
156
+ /**
157
+ * BobResponsiveness component acts as a UI for viewing the current breakpoint
158
+ * and modifying the global responsiveness configuration in LayoutContext.
159
+ */
160
+ declare const BobResponsiveness: React.FC<BobResponsivenessProps>;
161
+
162
+ /**
163
+ * A GapDropZone represents the empty space between two BobItems (or before the first / after the last).
164
+ * It renders as a 20px invisible hit-target that highlights when dragged over
165
+ * and contains the +/- column span control for the *preceding* item.
166
+ *
167
+ * `containerId` – the BobPanel this gap belongs to
168
+ * `insertAfterItemId` – if set, the new item is inserted AFTER this id; if undefined, insert at START
169
+ * `colSpan` – current colSpan of the preceding item (for display)
170
+ * `onResize` – called with delta (+1 / -1)
171
+ * `position` – 'start' | 'between' | 'end'
172
+ */
173
+ interface GapDropZoneProps {
174
+ id: string;
175
+ containerId: string;
176
+ insertAfterItemId?: string;
177
+ colSpan?: number;
178
+ newline?: boolean;
179
+ onResize?: (delta: number) => void;
180
+ onToggleNewline?: () => void;
181
+ position: 'start' | 'between' | 'end';
182
+ }
183
+ declare const GapDropZone: React.FC<GapDropZoneProps>;
184
+
185
+ export { BobForm, type BobFormProps, BobItem, type BobItemProps, BobPage, type BobPageProps, BobPanel, type BobPanelProps, BobResponsiveness, type BobResponsivenessConfig, type BobResponsivenessProps, BobTab, BobTabPage, type BobTabPageProps, type BobTabProps, BobTabStrip, type BobTabStripProps, DEFAULT_RESPONSIVENESS_CONFIG, GapDropZone, type GapDropZoneProps, type LayoutContextState, LayoutProvider, type LayoutStateDictionary, useLayoutContext };