@fewbox/den 0.2.0-preview.30 → 0.2.0-preview.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/.claude/skills/fewbox-den/SKILL.md +426 -413
- package/templates/FEWBOX.md +12 -34
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fewbox-den
|
|
3
|
-
description: Use when writing React UI with the fewbox-den component library. Provides component API reference for layouts (X/Y/S series), views (VLabel, VForm, VInput, etc.), network clients (Fetch, SSE, GraphQL, WebSocket), styling (ColorType, FontSizeType), and common patterns (page layout, forms, cards). Invoke this skill when you see
|
|
3
|
+
description: Use when writing React UI with the fewbox-den component library. Provides component API reference for layouts (X/Y/S series), views (VLabel, VForm, VInput, etc.), network clients (Fetch, SSE, GraphQL, WebSocket), styling (ColorType, FontSizeType), and common patterns (page layout, forms, cards). Invoke this skill when you see fewbox-den imports or need to create UI with fewbox-den.
|
|
4
4
|
allowed-tools: Read, Grep, Glob
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -10,26 +10,21 @@ When writing code that uses fewbox-den, follow the patterns and API documented b
|
|
|
10
10
|
|
|
11
11
|
## Import Pattern
|
|
12
12
|
|
|
13
|
-
All components
|
|
13
|
+
All components, enums, and types are named exports:
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
// Components accessed via:
|
|
21
|
-
Den.Components.XCenter
|
|
22
|
-
Den.Components.VLabel
|
|
23
|
-
Den.Components.ColorType // enums are also under Components
|
|
16
|
+
import { XCenter, VLabel, ColorType } from '@fewbox/den'; // core variant
|
|
17
|
+
import { XCenter, VLabel, GetFetch, boot } from '@fewbox/den-app'; // app variant (includes network/store)
|
|
18
|
+
import { XCenter, VLabel, Debug } from '@fewbox/den-web'; // web variant
|
|
24
19
|
```
|
|
25
20
|
|
|
26
21
|
### Variant Exports
|
|
27
22
|
|
|
28
|
-
- **core** (`
|
|
29
|
-
- **web** (`
|
|
30
|
-
- **app** (`
|
|
31
|
-
- **app** also exports:
|
|
32
|
-
- **append** (no
|
|
23
|
+
- **core** (`@fewbox/den`): Layout (X/Y/S/Flex/FlexItem/Position/PositionArea/Dock/BreakpointDisplay/Responsive), View (VBoundary, VLabel, VText, VForm, VActionForm, VInput/*, VSelect, VDropdown, VSwitch, VTextArea, VImage, VSvg, VBackground, VAnimation, VTooltip, VLoading, VErrorBoundary, VEllipse/VLine/VRectangle, VHyperlink, VMask, VFrame, VTheme, VBadge, VTemplate, VMount, VHeader/VNav/VMain/VAside/VFooter, VHR, VSection, VZone, VAudio, VVideo, VShadow), Enums, Types, utilities
|
|
24
|
+
- **web** (`@fewbox/den-web`): core + Debug, VGoogleFont, VChromeExtensionValidator, GoogleGrant, GoogleSignin, FigmaSignin, WeComSignin, parseJWT
|
|
25
|
+
- **app** (`@fewbox/den-app`): web + VDynamic, VAvatar, VBlock, VCard, VCardMedia, VCardSocial, VCardWindow, VPhoto, VStack, VTree
|
|
26
|
+
- **app** also exports: FewBox (boot, BootClass), Language, Network, Store modules
|
|
27
|
+
- **append** (`@fewbox/den-append`, no UI components): FewBox, Language, Network, Store modules only
|
|
33
28
|
|
|
34
29
|
## Core Concepts
|
|
35
30
|
|
|
@@ -196,53 +191,53 @@ All X-series components accept `gap`, `cross`, `itemsShrink` props in addition t
|
|
|
196
191
|
|
|
197
192
|
```tsx
|
|
198
193
|
// Basic horizontal layout (default: justify-content: flex-start)
|
|
199
|
-
<
|
|
194
|
+
<X gap="1em">
|
|
200
195
|
<Child1 /> <Child2 /> <Child3 />
|
|
201
|
-
</
|
|
196
|
+
</X>
|
|
202
197
|
|
|
203
198
|
// Justify-content variants:
|
|
204
|
-
<
|
|
205
|
-
<
|
|
206
|
-
<
|
|
207
|
-
<
|
|
208
|
-
<
|
|
209
|
-
<
|
|
199
|
+
<XLeft>...</XLeft> // flex-start
|
|
200
|
+
<XCenter>...</XCenter> // center
|
|
201
|
+
<XRight>...</XRight> // flex-end
|
|
202
|
+
<XBetween>...</XBetween> // space-between
|
|
203
|
+
<XAround>...</XAround> // space-around
|
|
204
|
+
<XEvenly>...</XEvenly> // space-evenly
|
|
210
205
|
|
|
211
206
|
// Auto-margin variants (margin-based centering):
|
|
212
|
-
<
|
|
213
|
-
<
|
|
214
|
-
<
|
|
215
|
-
<
|
|
216
|
-
<
|
|
217
|
-
<
|
|
207
|
+
<XAutoLeft>...</XAutoLeft> // first child margin-right: auto
|
|
208
|
+
<XAutoCenter>...</XAutoCenter> // center child with auto margins
|
|
209
|
+
<XAutoRight>...</XAutoRight> // last child margin-left: auto
|
|
210
|
+
<XAutoLefts>...</XAutoLefts> // all items margin-right: auto
|
|
211
|
+
<XAutoRights>...</XAutoRights> // all items margin-left: auto
|
|
212
|
+
<XAutoBoth>...</XAutoBoth> // first and last with auto margins
|
|
218
213
|
|
|
219
214
|
// Custom flex-basis distribution:
|
|
220
|
-
<
|
|
215
|
+
<XCustom basises={['20%', '30%', '50%']}>
|
|
221
216
|
<Child1 /> <Child2 /> <Child3 />
|
|
222
|
-
</
|
|
217
|
+
</XCustom>
|
|
223
218
|
|
|
224
219
|
// Uniform flex-basis with auto-margin sides:
|
|
225
|
-
<
|
|
220
|
+
<XCustomBoth basis="10%">
|
|
226
221
|
<Child1 /> <Child2 /> <Child3 />
|
|
227
|
-
</
|
|
222
|
+
</XCustomBoth>
|
|
228
223
|
|
|
229
224
|
// Wrap variants (flex-wrap: wrap with align-content):
|
|
230
|
-
<
|
|
231
|
-
<
|
|
232
|
-
<
|
|
233
|
-
<
|
|
234
|
-
<
|
|
235
|
-
<
|
|
225
|
+
<XWrapTop>...</XWrapTop> // align-content: flex-start
|
|
226
|
+
<XWrapMiddle>...</XWrapMiddle> // align-content: center
|
|
227
|
+
<XWrapBottom>...</XWrapBottom> // align-content: flex-end
|
|
228
|
+
<XWrapAround>...</XWrapAround> // align-content: space-around
|
|
229
|
+
<XWrapBetween>...</XWrapBetween> // align-content: space-between
|
|
230
|
+
<XWrapEvenly>...</XWrapEvenly> // align-content: space-evenly
|
|
236
231
|
```
|
|
237
232
|
|
|
238
233
|
**Cross-axis alignment** (align-items) via the `cross` prop on X components:
|
|
239
234
|
|
|
240
235
|
```tsx
|
|
241
|
-
<
|
|
242
|
-
<
|
|
243
|
-
<
|
|
244
|
-
<
|
|
245
|
-
<
|
|
236
|
+
<X cross={XCrossType.Top}> // align-items: flex-start
|
|
237
|
+
<X cross={XCrossType.Middle}> // align-items: center
|
|
238
|
+
<X cross={XCrossType.Bottom}> // align-items: flex-end
|
|
239
|
+
<X cross={XCrossType.Stretch}> // align-items: stretch
|
|
240
|
+
<X cross={XCrossType.Baseline}> // align-items: baseline
|
|
246
241
|
```
|
|
247
242
|
|
|
248
243
|
### Y-Series (Vertical Layout, flex-direction: column)
|
|
@@ -250,43 +245,43 @@ All X-series components accept `gap`, `cross`, `itemsShrink` props in addition t
|
|
|
250
245
|
Mirrors X-series but vertically:
|
|
251
246
|
|
|
252
247
|
```tsx
|
|
253
|
-
<
|
|
254
|
-
<
|
|
255
|
-
<
|
|
256
|
-
<
|
|
257
|
-
<
|
|
258
|
-
<
|
|
259
|
-
<
|
|
248
|
+
<Y gap="1em">...</Y> // default
|
|
249
|
+
<YTop>...</YTop> // justify-content: flex-start
|
|
250
|
+
<YMiddle>...</YMiddle> // justify-content: center
|
|
251
|
+
<YBottom>...</YBottom> // justify-content: flex-end
|
|
252
|
+
<YBetween>...</YBetween> // space-between
|
|
253
|
+
<YAround>...</YAround> // space-around
|
|
254
|
+
<YEvenly>...</YEvenly> // space-evenly
|
|
260
255
|
|
|
261
256
|
// Auto-margin variants:
|
|
262
|
-
<
|
|
263
|
-
<
|
|
264
|
-
<
|
|
265
|
-
<
|
|
266
|
-
<
|
|
267
|
-
<
|
|
257
|
+
<YAutoTop>...</YAutoTop>
|
|
258
|
+
<YAutoCenter>...</YAutoCenter>
|
|
259
|
+
<YAutoBottom>...</YAutoBottom>
|
|
260
|
+
<YAutoTops>...</YAutoTops>
|
|
261
|
+
<YAutoBottoms>...</YAutoBottoms>
|
|
262
|
+
<YAutoBoth>...</YAutoBoth>
|
|
268
263
|
|
|
269
264
|
// Custom:
|
|
270
|
-
<
|
|
271
|
-
<
|
|
265
|
+
<YCustom basises={['auto', '1fr', 'auto']}>...</YCustom>
|
|
266
|
+
<YCustomBoth basis="10%">...</YCustomBoth>
|
|
272
267
|
|
|
273
268
|
// Wrap variants:
|
|
274
|
-
<
|
|
275
|
-
<
|
|
276
|
-
<
|
|
277
|
-
<
|
|
278
|
-
<
|
|
279
|
-
<
|
|
269
|
+
<YWrapLeft>...</YWrapLeft>
|
|
270
|
+
<YWrapCenter>...</YWrapCenter>
|
|
271
|
+
<YWrapRight>...</YWrapRight>
|
|
272
|
+
<YWrapAround>...</YWrapAround>
|
|
273
|
+
<YWrapBetween>...</YWrapBetween>
|
|
274
|
+
<YWrapEvenly>...</YWrapEvenly>
|
|
280
275
|
```
|
|
281
276
|
|
|
282
277
|
**Cross-axis alignment** (align-items) via the `cross` prop on Y components:
|
|
283
278
|
|
|
284
279
|
```tsx
|
|
285
|
-
<
|
|
286
|
-
<
|
|
287
|
-
<
|
|
288
|
-
<
|
|
289
|
-
<
|
|
280
|
+
<Y cross={YCrossType.Left}> // align-items: flex-start
|
|
281
|
+
<Y cross={YCrossType.Center}> // align-items: center
|
|
282
|
+
<Y cross={YCrossType.Right}> // align-items: flex-end
|
|
283
|
+
<Y cross={YCrossType.Stretch}> // align-items: stretch
|
|
284
|
+
<Y cross={YCrossType.Baseline}> // align-items: baseline
|
|
290
285
|
```
|
|
291
286
|
|
|
292
287
|
### S-Series (Individual Item Cross-axis Override)
|
|
@@ -294,36 +289,36 @@ Mirrors X-series but vertically:
|
|
|
294
289
|
Wrap a child in an S-component to override the parent's cross-axis alignment for that child only:
|
|
295
290
|
|
|
296
291
|
```tsx
|
|
297
|
-
<
|
|
292
|
+
<XCenter cross={XCrossType.Bottom} height="6em">
|
|
298
293
|
<Child1 /> {/* aligned to bottom */}
|
|
299
294
|
<Child2 /> {/* aligned to bottom */}
|
|
300
|
-
<
|
|
295
|
+
<STop>
|
|
301
296
|
<Child3 /> {/* overridden to top */}
|
|
302
|
-
</
|
|
303
|
-
</
|
|
297
|
+
</STop>
|
|
298
|
+
</XCenter>
|
|
304
299
|
|
|
305
300
|
// Available S-components:
|
|
306
|
-
<
|
|
307
|
-
<
|
|
308
|
-
<
|
|
309
|
-
<
|
|
310
|
-
<
|
|
311
|
-
<
|
|
312
|
-
<
|
|
313
|
-
<
|
|
301
|
+
<STop>...</STop> // align-self: flex-start
|
|
302
|
+
<SMiddle>...</SMiddle> // align-self: center
|
|
303
|
+
<SBottom>...</SBottom> // align-self: flex-end
|
|
304
|
+
<SLeft>...</SLeft> // align-self: flex-start (in Y container)
|
|
305
|
+
<SCenter>...</SCenter> // align-self: center (in Y container)
|
|
306
|
+
<SRight>...</SRight> // align-self: flex-end (in Y container)
|
|
307
|
+
<SStretch>...</SStretch> // align-self: stretch
|
|
308
|
+
<SBaseline>...</SBaseline>// align-self: baseline
|
|
314
309
|
```
|
|
315
310
|
|
|
316
311
|
### Position & PositionArea
|
|
317
312
|
|
|
318
313
|
```tsx
|
|
319
314
|
// PositionArea establishes a positioning context (position: relative) on its child.
|
|
320
|
-
<
|
|
321
|
-
<
|
|
322
|
-
<
|
|
315
|
+
<PositionArea category={PositionAreaCategory.FullSize}>
|
|
316
|
+
<VBoundary height="250px" backgroundColor={ColorType.Dark}>
|
|
317
|
+
<Position category={PositionCategory.Area} type={PositionType.Center}>
|
|
323
318
|
<Content />
|
|
324
|
-
</
|
|
325
|
-
</
|
|
326
|
-
</
|
|
319
|
+
</Position>
|
|
320
|
+
</VBoundary>
|
|
321
|
+
</PositionArea>
|
|
327
322
|
|
|
328
323
|
// PositionAreaCategory:
|
|
329
324
|
// Default = inline-block container
|
|
@@ -339,14 +334,14 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
339
334
|
// Top, Right, Bottom, Left, LeftTop, RightTop, LeftBottom, RightBottom, Center
|
|
340
335
|
|
|
341
336
|
// Custom positioning (omit type, set top/right/bottom/left):
|
|
342
|
-
<
|
|
337
|
+
<Position category={PositionCategory.Area} top="20px" left="30px">
|
|
343
338
|
<Content />
|
|
344
|
-
</
|
|
339
|
+
</Position>
|
|
345
340
|
|
|
346
341
|
// Sticky navbar example:
|
|
347
|
-
<
|
|
342
|
+
<Position category={PositionCategory.Edge} top="0px" isFullWidth zIndex={10}>
|
|
348
343
|
<NavBar />
|
|
349
|
-
</
|
|
344
|
+
</Position>
|
|
350
345
|
|
|
351
346
|
// Position props: category (required), type, top, right, bottom, left, zIndex, isFullWidth
|
|
352
347
|
```
|
|
@@ -356,13 +351,13 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
356
351
|
```tsx
|
|
357
352
|
// Dock positions an overlay relative to its children.
|
|
358
353
|
// renderOverlay is REQUIRED — returns the overlay element.
|
|
359
|
-
<
|
|
360
|
-
category={
|
|
354
|
+
<Dock
|
|
355
|
+
category={DockCategory.Top}
|
|
361
356
|
offset={4}
|
|
362
357
|
renderOverlay={() => <div>Tooltip above</div>}
|
|
363
358
|
>
|
|
364
359
|
<button>Hover me</button>
|
|
365
|
-
</
|
|
360
|
+
</Dock>
|
|
366
361
|
|
|
367
362
|
// DockCategory:
|
|
368
363
|
// Top, Right, Bottom, Left
|
|
@@ -373,15 +368,33 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
373
368
|
// DockAlignment (only for corner categories like LeftTop, RightTop, etc.):
|
|
374
369
|
// Horizontal, Vertical
|
|
375
370
|
|
|
371
|
+
// DockPopoverMode (controls overlay visibility behavior):
|
|
372
|
+
// Manual — overlay visibility controlled by isOverlayVisible prop
|
|
373
|
+
// Auto — overlay uses CSS Anchor Positioning with browser popover API
|
|
374
|
+
|
|
376
375
|
// Corner with alignment:
|
|
377
|
-
<
|
|
378
|
-
category={
|
|
379
|
-
alignment={
|
|
376
|
+
<Dock
|
|
377
|
+
category={DockCategory.LeftTop}
|
|
378
|
+
alignment={DockAlignment.Horizontal}
|
|
380
379
|
offset={4}
|
|
381
380
|
renderOverlay={() => <DropdownMenu />}
|
|
382
381
|
>
|
|
383
382
|
<TriggerButton />
|
|
384
|
-
</
|
|
383
|
+
</Dock>
|
|
384
|
+
|
|
385
|
+
// With popover mode and visibility control:
|
|
386
|
+
<Dock
|
|
387
|
+
category={DockCategory.Bottom}
|
|
388
|
+
offset={4}
|
|
389
|
+
isOverlayVisible={isOpen}
|
|
390
|
+
popoverMode={DockPopoverMode.Manual}
|
|
391
|
+
overlayZIndex={9999}
|
|
392
|
+
isPortal={false}
|
|
393
|
+
onOverlayClose={() => setIsOpen(false)}
|
|
394
|
+
renderOverlay={() => <DropdownContent />}
|
|
395
|
+
>
|
|
396
|
+
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
|
|
397
|
+
</Dock>
|
|
385
398
|
```
|
|
386
399
|
|
|
387
400
|
### BreakpointDisplay (Responsive Show/Hide)
|
|
@@ -394,40 +407,40 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
394
407
|
// ScreenSizeType: ExtraSmall, Small, Medium, Large, ExtraLarge, ExtraExtraLarge
|
|
395
408
|
|
|
396
409
|
// Always visible
|
|
397
|
-
<
|
|
410
|
+
<BreakpointDisplay category={BreakpointCategory.Visible}>
|
|
398
411
|
<div>Always shown</div>
|
|
399
|
-
</
|
|
412
|
+
</BreakpointDisplay>
|
|
400
413
|
|
|
401
414
|
// Always hidden
|
|
402
|
-
<
|
|
415
|
+
<BreakpointDisplay category={BreakpointCategory.Hidden}>
|
|
403
416
|
<div>Always hidden</div>
|
|
404
|
-
</
|
|
417
|
+
</BreakpointDisplay>
|
|
405
418
|
|
|
406
419
|
// Responsive: visible only on Medium breakpoint
|
|
407
|
-
<
|
|
408
|
-
category={
|
|
409
|
-
type={
|
|
410
|
-
breakpoint={
|
|
420
|
+
<BreakpointDisplay
|
|
421
|
+
category={BreakpointCategory.Visible}
|
|
422
|
+
type={BreakpointType.Only}
|
|
423
|
+
breakpoint={ScreenSizeType.Medium}
|
|
411
424
|
>
|
|
412
425
|
<div>Medium only</div>
|
|
413
|
-
</
|
|
426
|
+
</BreakpointDisplay>
|
|
414
427
|
|
|
415
428
|
// Hidden on Small and below
|
|
416
|
-
<
|
|
417
|
-
category={
|
|
418
|
-
type={
|
|
419
|
-
breakpoint={
|
|
429
|
+
<BreakpointDisplay
|
|
430
|
+
category={BreakpointCategory.Hidden}
|
|
431
|
+
type={BreakpointType.Down}
|
|
432
|
+
breakpoint={ScreenSizeType.Small}
|
|
420
433
|
>
|
|
421
434
|
<div>Hidden on mobile</div>
|
|
422
|
-
</
|
|
435
|
+
</BreakpointDisplay>
|
|
423
436
|
|
|
424
437
|
// Multiple breakpoints (array):
|
|
425
|
-
<
|
|
426
|
-
category={
|
|
427
|
-
breakpoint={[
|
|
438
|
+
<BreakpointDisplay
|
|
439
|
+
category={BreakpointCategory.Visible}
|
|
440
|
+
breakpoint={[ScreenSizeType.Small, ScreenSizeType.Large]}
|
|
428
441
|
>
|
|
429
442
|
<div>Visible on Small and Large</div>
|
|
430
|
-
</
|
|
443
|
+
</BreakpointDisplay>
|
|
431
444
|
```
|
|
432
445
|
|
|
433
446
|
### Responsive (Desktop/Mobile Switch)
|
|
@@ -435,8 +448,8 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
435
448
|
```tsx
|
|
436
449
|
// Shorthand for showing desktop or mobile content based on breakpoint.
|
|
437
450
|
// Children must accept className prop (uses BreakpointDisplay internally).
|
|
438
|
-
<
|
|
439
|
-
breakpointType={
|
|
451
|
+
<Responsive
|
|
452
|
+
breakpointType={ScreenSizeType.Medium}
|
|
440
453
|
desktop={<div>Desktop layout</div>}
|
|
441
454
|
mobile={<div>Mobile layout</div>}
|
|
442
455
|
/>
|
|
@@ -448,9 +461,9 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
448
461
|
// Like Bootstrap's Container — constrains max-width and centers content.
|
|
449
462
|
// Category controls at which breakpoint max-width starts applying.
|
|
450
463
|
// Below the breakpoint: full width. Above: max-width grows per tier.
|
|
451
|
-
<
|
|
464
|
+
<VZone category={ZoneCategory.Small} padding="1em">
|
|
452
465
|
<Content />
|
|
453
|
-
</
|
|
466
|
+
</VZone>
|
|
454
467
|
|
|
455
468
|
// ZoneCategory:
|
|
456
469
|
// Fluid — always 100%, no max-width
|
|
@@ -465,20 +478,20 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
465
478
|
|
|
466
479
|
```tsx
|
|
467
480
|
// Flex — full flexbox control
|
|
468
|
-
<
|
|
469
|
-
direction={
|
|
470
|
-
wrap={
|
|
471
|
-
justifyContent={
|
|
472
|
-
alignItems={
|
|
481
|
+
<Flex
|
|
482
|
+
direction={FlexDirectionType.Row}
|
|
483
|
+
wrap={FlexWrapType.Wrap}
|
|
484
|
+
justifyContent={FlexJustifyContentType.SpaceBetween}
|
|
485
|
+
alignItems={FlexAlignItemsType.Center}
|
|
473
486
|
gap="1em"
|
|
474
487
|
>
|
|
475
488
|
...
|
|
476
|
-
</
|
|
489
|
+
</Flex>
|
|
477
490
|
|
|
478
491
|
// FlexItem — wraps a child with flex item properties
|
|
479
|
-
<
|
|
492
|
+
<FlexItem grow={1} shrink={0} basis="200px" order={2}>
|
|
480
493
|
<Content />
|
|
481
|
-
</
|
|
494
|
+
</FlexItem>
|
|
482
495
|
|
|
483
496
|
// FlexItem also exports FlexItemAlignSelfType
|
|
484
497
|
// Flex also exports FlexAlignContentType
|
|
@@ -493,14 +506,14 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
493
506
|
The basic div wrapper. Use as a general-purpose container:
|
|
494
507
|
|
|
495
508
|
```tsx
|
|
496
|
-
<
|
|
509
|
+
<VBoundary
|
|
497
510
|
padding="1em"
|
|
498
|
-
backgroundColor={
|
|
499
|
-
borderRadius={
|
|
511
|
+
backgroundColor={ColorType.Light}
|
|
512
|
+
borderRadius={BorderRadiusType.Default}
|
|
500
513
|
width="100%"
|
|
501
514
|
>
|
|
502
515
|
<Content />
|
|
503
|
-
</
|
|
516
|
+
</VBoundary>
|
|
504
517
|
```
|
|
505
518
|
|
|
506
519
|
### VLabel (Text/Label)
|
|
@@ -509,45 +522,45 @@ For inline text. Renders as `<span>` by default, configurable to any inline/head
|
|
|
509
522
|
|
|
510
523
|
```tsx
|
|
511
524
|
// Basic label
|
|
512
|
-
<
|
|
525
|
+
<VLabel caption="Hello World" />
|
|
513
526
|
|
|
514
527
|
// With heading category
|
|
515
|
-
<
|
|
516
|
-
category={
|
|
528
|
+
<VLabel
|
|
529
|
+
category={LabelCategory.H1}
|
|
517
530
|
caption="Page Title"
|
|
518
531
|
/>
|
|
519
532
|
|
|
520
533
|
// Styled label
|
|
521
|
-
<
|
|
522
|
-
fontSize={
|
|
523
|
-
fontWeight={
|
|
524
|
-
frontColor={
|
|
534
|
+
<VLabel
|
|
535
|
+
fontSize={FontSizeType.Large}
|
|
536
|
+
fontWeight={FontWeightType.Bold}
|
|
537
|
+
frontColor={ColorType.Primary}
|
|
525
538
|
caption="Important Text"
|
|
526
539
|
/>
|
|
527
540
|
|
|
528
541
|
// Circle label (badge)
|
|
529
|
-
<
|
|
530
|
-
type={
|
|
531
|
-
frontColor={
|
|
532
|
-
backgroundColor={
|
|
542
|
+
<VLabel
|
|
543
|
+
type={LabelType.Circle}
|
|
544
|
+
frontColor={ColorType.White}
|
|
545
|
+
backgroundColor={ColorType.Black}
|
|
533
546
|
caption="X"
|
|
534
547
|
/>
|
|
535
548
|
|
|
536
549
|
// Text overflow (ellipsis)
|
|
537
|
-
<
|
|
550
|
+
<VLabel
|
|
538
551
|
isTextOverflow
|
|
539
552
|
width="12em"
|
|
540
553
|
caption="This very long text will be truncated with ellipsis..."
|
|
541
554
|
/>
|
|
542
555
|
|
|
543
556
|
// Alignment
|
|
544
|
-
<
|
|
545
|
-
alignType={
|
|
557
|
+
<VLabel
|
|
558
|
+
alignType={LabelAlignType.Center}
|
|
546
559
|
caption="Centered text"
|
|
547
560
|
/>
|
|
548
561
|
|
|
549
562
|
// i18n with React Intl
|
|
550
|
-
<
|
|
563
|
+
<VLabel caption={<FormattedMessage id="Label.Hello" />} />
|
|
551
564
|
|
|
552
565
|
// Available categories:
|
|
553
566
|
// LabelCategory.Div, Span, H1-H6, Abbr, Strong, Em, Code, Pre,
|
|
@@ -559,9 +572,9 @@ For inline text. Renders as `<span>` by default, configurable to any inline/head
|
|
|
559
572
|
// that apply `inherit` to the inner heading element, overriding
|
|
560
573
|
// browser defaults. Without these props, headings keep their
|
|
561
574
|
// browser default styles (e.g., H1 is bold and 2em).
|
|
562
|
-
<
|
|
563
|
-
category={
|
|
564
|
-
fontWeight={
|
|
575
|
+
<VLabel
|
|
576
|
+
category={LabelCategory.H1}
|
|
577
|
+
fontWeight={FontWeightType.Thin}
|
|
565
578
|
caption="Thin H1"
|
|
566
579
|
/>
|
|
567
580
|
```
|
|
@@ -571,13 +584,13 @@ For inline text. Renders as `<span>` by default, configurable to any inline/head
|
|
|
571
584
|
For block-level text (p, article, section, etc.):
|
|
572
585
|
|
|
573
586
|
```tsx
|
|
574
|
-
<
|
|
575
|
-
category={
|
|
587
|
+
<VText
|
|
588
|
+
category={TextCategory.P}
|
|
576
589
|
caption="Paragraph text content."
|
|
577
590
|
/>
|
|
578
591
|
|
|
579
|
-
<
|
|
580
|
-
alignType={
|
|
592
|
+
<VText
|
|
593
|
+
alignType={TextAlignType.Center}
|
|
581
594
|
renderContent={() => <span>Complex <strong>content</strong></span>}
|
|
582
595
|
/>
|
|
583
596
|
```
|
|
@@ -586,20 +599,20 @@ For block-level text (p, article, section, etc.):
|
|
|
586
599
|
|
|
587
600
|
```tsx
|
|
588
601
|
// Renders an <a> tag. Requires category and to props.
|
|
589
|
-
<
|
|
590
|
-
category={
|
|
602
|
+
<VHyperlink
|
|
603
|
+
category={HyperlinkCategory.Self}
|
|
591
604
|
to="/about"
|
|
592
605
|
>
|
|
593
606
|
Click me
|
|
594
|
-
</
|
|
607
|
+
</VHyperlink>
|
|
595
608
|
|
|
596
609
|
// Open in new window
|
|
597
|
-
<
|
|
598
|
-
category={
|
|
610
|
+
<VHyperlink
|
|
611
|
+
category={HyperlinkCategory.NewWindow}
|
|
599
612
|
to="https://example.com"
|
|
600
613
|
>
|
|
601
614
|
External link
|
|
602
|
-
</
|
|
615
|
+
</VHyperlink>
|
|
603
616
|
|
|
604
617
|
// HyperlinkCategory: NewWindow (_blank), Self (_self), Parent (_parent), Top (_top)
|
|
605
618
|
```
|
|
@@ -609,7 +622,7 @@ For block-level text (p, article, section, etc.):
|
|
|
609
622
|
#### VForm (Form Container)
|
|
610
623
|
|
|
611
624
|
```tsx
|
|
612
|
-
<
|
|
625
|
+
<VForm
|
|
613
626
|
handleSubmit={(data) => {
|
|
614
627
|
// data is automatically converted from FormData to nested JSON
|
|
615
628
|
// e.g., { form: { username: "value", password: "value" } }
|
|
@@ -621,21 +634,21 @@ For block-level text (p, article, section, etc.):
|
|
|
621
634
|
invalidInputs[0]?.focus();
|
|
622
635
|
}}
|
|
623
636
|
>
|
|
624
|
-
<
|
|
625
|
-
<
|
|
626
|
-
<
|
|
627
|
-
<
|
|
628
|
-
</
|
|
629
|
-
</
|
|
637
|
+
<Y gap="0.5em">
|
|
638
|
+
<VTextBox name="form.username" placeholder="Username" />
|
|
639
|
+
<VPassword name="form.password" placeholder="Password" />
|
|
640
|
+
<VSubmit caption="Submit" />
|
|
641
|
+
</Y>
|
|
642
|
+
</VForm>
|
|
630
643
|
|
|
631
644
|
// HandleSubmitCategory: Json (default), FormData
|
|
632
645
|
// Use FormData category to receive raw FormData instead of parsed JSON:
|
|
633
|
-
<
|
|
634
|
-
handleSubmitCategory={
|
|
646
|
+
<VForm
|
|
647
|
+
handleSubmitCategory={HandleSubmitCategory.FormData}
|
|
635
648
|
handleSubmit={(formData) => { /* formData is FormData */ }}
|
|
636
649
|
>
|
|
637
650
|
...
|
|
638
|
-
</
|
|
651
|
+
</VForm>
|
|
639
652
|
```
|
|
640
653
|
|
|
641
654
|
#### VActionForm (Simple Form)
|
|
@@ -643,10 +656,10 @@ For block-level text (p, article, section, etc.):
|
|
|
643
656
|
A simple `<form>` wrapper without JSON conversion or validation handling:
|
|
644
657
|
|
|
645
658
|
```tsx
|
|
646
|
-
<
|
|
659
|
+
<VActionForm action="/api/submit" method="POST">
|
|
647
660
|
<input name="field" />
|
|
648
661
|
<button type="submit">Submit</button>
|
|
649
|
-
</
|
|
662
|
+
</VActionForm>
|
|
650
663
|
```
|
|
651
664
|
|
|
652
665
|
**Form data naming conventions:**
|
|
@@ -658,7 +671,7 @@ A simple `<form>` wrapper without JSON conversion or validation handling:
|
|
|
658
671
|
|
|
659
672
|
```tsx
|
|
660
673
|
// Text input
|
|
661
|
-
<
|
|
674
|
+
<VTextBox
|
|
662
675
|
name="form.name"
|
|
663
676
|
placeholder="Enter name"
|
|
664
677
|
label="Name" // Floating label
|
|
@@ -670,70 +683,70 @@ A simple `<form>` wrapper without JSON conversion or validation handling:
|
|
|
670
683
|
|
|
671
684
|
// Controlled input with hooks
|
|
672
685
|
const [value, setValue] = useState('');
|
|
673
|
-
<
|
|
686
|
+
<VTextBox
|
|
674
687
|
name="form.search"
|
|
675
688
|
valueHook={value}
|
|
676
689
|
setStateHook={setValue}
|
|
677
690
|
/>
|
|
678
691
|
|
|
679
692
|
// With suffix options
|
|
680
|
-
<
|
|
693
|
+
<VTextBox
|
|
681
694
|
renderOptions={(inputRef) => (
|
|
682
|
-
<
|
|
695
|
+
<VSvg onClick={() => alert(inputRef.current?.value)}>
|
|
683
696
|
<SearchIcon />
|
|
684
|
-
</
|
|
697
|
+
</VSvg>
|
|
685
698
|
)}
|
|
686
699
|
/>
|
|
687
700
|
|
|
688
701
|
// Other input types:
|
|
689
|
-
<
|
|
690
|
-
<
|
|
691
|
-
<
|
|
692
|
-
<
|
|
693
|
-
<
|
|
694
|
-
<
|
|
695
|
-
<
|
|
696
|
-
<
|
|
697
|
-
<
|
|
698
|
-
<
|
|
699
|
-
<
|
|
700
|
-
<
|
|
701
|
-
<
|
|
702
|
+
<VEmail name="form.email" required placeholder="Email" />
|
|
703
|
+
<VPassword name="form.password" placeholder="Password" />
|
|
704
|
+
<VNumber name="form.count" min={0} max={100} />
|
|
705
|
+
<VDate name="form.date" />
|
|
706
|
+
<VDatetimeLocal name="form.datetime" />
|
|
707
|
+
<VTime name="form.time" />
|
|
708
|
+
<VMonth name="form.month" />
|
|
709
|
+
<VWeek name="form.week" />
|
|
710
|
+
<VColor name="form.color" />
|
|
711
|
+
<VTel name="form.phone" />
|
|
712
|
+
<VUrl name="form.website" />
|
|
713
|
+
<VSearch name="form.search" />
|
|
714
|
+
<VRange name="form.volume" min={0} max={100} />
|
|
702
715
|
|
|
703
716
|
// TextArea
|
|
704
|
-
<
|
|
717
|
+
<VTextArea name="form.bio" rows={6} placeholder="Bio" />
|
|
705
718
|
|
|
706
719
|
// Checkbox
|
|
707
|
-
<
|
|
720
|
+
<VCheckBox
|
|
708
721
|
id="form.agree"
|
|
709
722
|
name="form.agree"
|
|
710
723
|
label="I agree"
|
|
711
724
|
isChecked={true}
|
|
712
|
-
frontColor={
|
|
725
|
+
frontColor={ColorType.Primary}
|
|
713
726
|
/>
|
|
714
727
|
|
|
715
728
|
// Radio
|
|
716
|
-
<
|
|
717
|
-
<
|
|
729
|
+
<VRadio id="opt1" name="form.option" value="A" label="Option A" />
|
|
730
|
+
<VRadio id="opt2" name="form.option" value="B" label="Option B" />
|
|
718
731
|
|
|
719
732
|
// Radio Group (managed state)
|
|
720
|
-
<
|
|
733
|
+
<VGroup
|
|
721
734
|
selectedValue="A"
|
|
722
735
|
renderGroup={(selectedValue, handleSelect) => (
|
|
723
736
|
<>
|
|
724
|
-
<
|
|
737
|
+
<VRadio id="opt1" name="form.option" value="A" label="A"
|
|
725
738
|
isChecked={selectedValue === 'A'} onChange={handleSelect} />
|
|
726
|
-
<
|
|
739
|
+
<VRadio id="opt2" name="form.option" value="B" label="B"
|
|
727
740
|
isChecked={selectedValue === 'B'} onChange={handleSelect} />
|
|
728
741
|
</>
|
|
729
742
|
)}
|
|
730
743
|
/>
|
|
731
744
|
|
|
732
745
|
// Hidden field
|
|
733
|
-
<
|
|
746
|
+
<VHidden name="form.id" value="123" />
|
|
734
747
|
|
|
735
748
|
// Select (native <select>)
|
|
736
|
-
<
|
|
749
|
+
<VSelect
|
|
737
750
|
name="form.country"
|
|
738
751
|
items={[
|
|
739
752
|
{ caption: 'USA', value: 'us' },
|
|
@@ -744,7 +757,7 @@ const [value, setValue] = useState('');
|
|
|
744
757
|
// ISelectItemData: { caption: string; value: string }
|
|
745
758
|
|
|
746
759
|
// Dropdown (custom dropdown with icons)
|
|
747
|
-
<
|
|
760
|
+
<VDropdown
|
|
748
761
|
name="form.category"
|
|
749
762
|
items={[
|
|
750
763
|
{ caption: 'Option A', value: 'a', icon: <IconA /> },
|
|
@@ -757,7 +770,7 @@ const [value, setValue] = useState('');
|
|
|
757
770
|
readOnly // renders as VLabel (non-interactive)
|
|
758
771
|
isSelectOnly // prevents manual typing, only allows selection from items
|
|
759
772
|
menuGap="4px" // gap between dropdown menu and trigger
|
|
760
|
-
menuBackgroundColor={
|
|
773
|
+
menuBackgroundColor={ColorType.White}
|
|
761
774
|
menuZIndex={9999}
|
|
762
775
|
handleChange={(value) => console.log(value)}
|
|
763
776
|
overWriteDropdownMenu={(items) => <CustomMenu items={items} />} // custom menu renderer
|
|
@@ -766,14 +779,14 @@ const [value, setValue] = useState('');
|
|
|
766
779
|
// IDropdownItemData: { caption: string; value: string; icon?: JSX.Element }
|
|
767
780
|
|
|
768
781
|
// Switch
|
|
769
|
-
<
|
|
782
|
+
<VSwitch
|
|
770
783
|
isOn={true}
|
|
771
784
|
icon={<SwitchIcon />}
|
|
772
785
|
onSwitch={(isOn) => console.log(isOn)}
|
|
773
786
|
/>
|
|
774
787
|
|
|
775
788
|
// File upload
|
|
776
|
-
<
|
|
789
|
+
<VFile
|
|
777
790
|
name="form.avatar"
|
|
778
791
|
isPreview // Show image preview
|
|
779
792
|
accept=".jpg,.png"
|
|
@@ -782,70 +795,70 @@ const [value, setValue] = useState('');
|
|
|
782
795
|
// FileCategory enum available for file categories
|
|
783
796
|
|
|
784
797
|
// File with base64 output
|
|
785
|
-
<
|
|
798
|
+
<VBase64File
|
|
786
799
|
name="form.file"
|
|
787
800
|
accept=".jpg,.jpeg,.png"
|
|
788
801
|
base64Image="iVBOR..." // Default base64 preview
|
|
789
802
|
/>
|
|
790
803
|
|
|
791
804
|
// Submit button
|
|
792
|
-
<
|
|
805
|
+
<VSubmit
|
|
793
806
|
caption="Submit"
|
|
794
|
-
fontSize={
|
|
795
|
-
backgroundColor={
|
|
796
|
-
frontColor={
|
|
807
|
+
fontSize={FontSizeType.Small}
|
|
808
|
+
backgroundColor={ColorType.Primary}
|
|
809
|
+
frontColor={ColorType.White}
|
|
797
810
|
/>
|
|
798
811
|
|
|
799
812
|
// Submit wrapper (wraps custom body with hidden submit input)
|
|
800
|
-
<
|
|
813
|
+
<VSubmitWrapper body={<button>Custom Submit UI</button>} />
|
|
801
814
|
```
|
|
802
815
|
|
|
803
816
|
### Media Components
|
|
804
817
|
|
|
805
818
|
```tsx
|
|
806
819
|
// Image
|
|
807
|
-
<
|
|
820
|
+
<VImage
|
|
808
821
|
src="/path/to/image.jpg"
|
|
809
822
|
alt="Description"
|
|
810
|
-
category={
|
|
823
|
+
category={ImageCategory.Circle} // Circle, Squared, FullSize, Scale
|
|
811
824
|
/>
|
|
812
825
|
|
|
813
826
|
// SVG (wraps imported SVG components)
|
|
814
827
|
import IconSvg from './icon.svg';
|
|
815
|
-
<
|
|
816
|
-
fontSize={
|
|
817
|
-
frontColor={
|
|
828
|
+
<VSvg
|
|
829
|
+
fontSize={FontSizeType.Large}
|
|
830
|
+
frontColor={ColorType.Primary}
|
|
818
831
|
>
|
|
819
832
|
<IconSvg />
|
|
820
|
-
</
|
|
833
|
+
</VSvg>
|
|
821
834
|
// SvgCategory enum available for SVG categories
|
|
822
835
|
|
|
823
836
|
// Background image
|
|
824
|
-
<
|
|
837
|
+
<VBackground
|
|
825
838
|
image="/path/to/bg.jpg"
|
|
826
839
|
imageWidth="100%"
|
|
827
840
|
imageHeight="400px"
|
|
828
|
-
position={
|
|
841
|
+
position={BackgroundPositionType.Center}
|
|
829
842
|
>
|
|
830
843
|
<OverlayContent />
|
|
831
|
-
</
|
|
844
|
+
</VBackground>
|
|
832
845
|
|
|
833
846
|
// Audio / Video
|
|
834
|
-
<
|
|
835
|
-
<
|
|
847
|
+
<VAudio src="/audio.mp3" />
|
|
848
|
+
<VVideo src="/video.mp4" />
|
|
836
849
|
```
|
|
837
850
|
|
|
838
851
|
### VTooltip
|
|
839
852
|
|
|
840
853
|
```tsx
|
|
841
854
|
// Tooltip uses Dock internally. Requires renderOverlay and dockCategory.
|
|
842
|
-
<
|
|
843
|
-
category={
|
|
844
|
-
dockCategory={
|
|
855
|
+
<VTooltip
|
|
856
|
+
category={TooltipCategory.Hover}
|
|
857
|
+
dockCategory={DockCategory.Top}
|
|
845
858
|
renderOverlay={() => <div>Tooltip content</div>}
|
|
846
859
|
>
|
|
847
860
|
<button>Hover me</button>
|
|
848
|
-
</
|
|
861
|
+
</VTooltip>
|
|
849
862
|
|
|
850
863
|
// TooltipCategory: Hover (default), Click
|
|
851
864
|
```
|
|
@@ -854,14 +867,14 @@ import IconSvg from './icon.svg';
|
|
|
854
867
|
|
|
855
868
|
```tsx
|
|
856
869
|
// Uses animate.css library. category is required.
|
|
857
|
-
<
|
|
858
|
-
category={
|
|
859
|
-
speed={
|
|
860
|
-
delay={
|
|
861
|
-
repeat={
|
|
870
|
+
<VAnimation
|
|
871
|
+
category={AnimationCategory.Bounce}
|
|
872
|
+
speed={AnimationSpeed.Fast}
|
|
873
|
+
delay={AnimationDelay.Delay1Second}
|
|
874
|
+
repeat={AnimationRepeat.Infinite}
|
|
862
875
|
>
|
|
863
876
|
<Content />
|
|
864
|
-
</
|
|
877
|
+
</VAnimation>
|
|
865
878
|
|
|
866
879
|
// AnimationCategory: Bounce, Flash, Pulse, RubberBand, ShakeX, Swing, Tada,
|
|
867
880
|
// Wobble, Jello, HeartBeat, FadeIn, FadeOut, ZoomIn, ZoomOut,
|
|
@@ -875,23 +888,23 @@ import IconSvg from './icon.svg';
|
|
|
875
888
|
### Shape Components
|
|
876
889
|
|
|
877
890
|
```tsx
|
|
878
|
-
<
|
|
891
|
+
<VEllipse width="100px" height="100px" backgroundColor={ColorType.Primary} />
|
|
879
892
|
// EllipseCategory enum available
|
|
880
893
|
|
|
881
|
-
<
|
|
894
|
+
<VRectangle width="200px" height="50px" backgroundColor={ColorType.Info} />
|
|
882
895
|
|
|
883
|
-
<
|
|
896
|
+
<VLine />
|
|
884
897
|
// LineCategory enum available
|
|
885
898
|
|
|
886
|
-
<
|
|
899
|
+
<VShadow category={ShadowCategory.Small}><Content /></VShadow>
|
|
887
900
|
```
|
|
888
901
|
|
|
889
902
|
### VTheme (Theme Provider)
|
|
890
903
|
|
|
891
904
|
```tsx
|
|
892
|
-
<
|
|
905
|
+
<VTheme category={ThemeCategory.Light}>
|
|
893
906
|
<App />
|
|
894
|
-
</
|
|
907
|
+
</VTheme>
|
|
895
908
|
|
|
896
909
|
// ThemeCategory: Light, Dark (or custom)
|
|
897
910
|
// ThemeVariables type available for typed CSS variable overrides
|
|
@@ -901,53 +914,53 @@ import IconSvg from './icon.svg';
|
|
|
901
914
|
|
|
902
915
|
```tsx
|
|
903
916
|
// VCard — structured card with head and body
|
|
904
|
-
<
|
|
905
|
-
head={<
|
|
906
|
-
body={<
|
|
917
|
+
<VCard
|
|
918
|
+
head={<VLabel caption="Card Title" />}
|
|
919
|
+
body={<VText caption="Card content" />}
|
|
907
920
|
isDraggable={false}
|
|
908
921
|
/>
|
|
909
922
|
|
|
910
923
|
// VCardMedia — card with media, head, body, foot sections
|
|
911
|
-
<
|
|
912
|
-
media={<
|
|
913
|
-
head={<
|
|
914
|
-
body={<
|
|
924
|
+
<VCardMedia
|
|
925
|
+
media={<VImage src="/image.jpg" category={ImageCategory.FullSize} />}
|
|
926
|
+
head={<VLabel caption="Title" />}
|
|
927
|
+
body={<VText caption="Description" />}
|
|
915
928
|
foot={<button>Action</button>}
|
|
916
929
|
/>
|
|
917
930
|
|
|
918
931
|
// VCardSocial — social media card variant
|
|
919
|
-
<
|
|
932
|
+
<VCardSocial ... />
|
|
920
933
|
|
|
921
934
|
// VCardWindow — card with background image
|
|
922
|
-
<
|
|
935
|
+
<VCardWindow
|
|
923
936
|
backgroundUrl="/bg.jpg"
|
|
924
|
-
backgroundSize={
|
|
937
|
+
backgroundSize={CardWindowSizeType.Cover}
|
|
925
938
|
>
|
|
926
939
|
<OverlayContent />
|
|
927
|
-
</
|
|
940
|
+
</VCardWindow>
|
|
928
941
|
|
|
929
942
|
// VBlock — structured block with icon, action, caption, subCaption
|
|
930
|
-
<
|
|
943
|
+
<VBlock
|
|
931
944
|
icon={<Icon />}
|
|
932
945
|
action={<button>Edit</button>}
|
|
933
|
-
caption={<
|
|
934
|
-
subCaption={<
|
|
946
|
+
caption={<VLabel caption="Title" />}
|
|
947
|
+
subCaption={<VLabel caption="Subtitle" />}
|
|
935
948
|
/>
|
|
936
949
|
|
|
937
950
|
// VStack — stacks children with z-index layering
|
|
938
|
-
<
|
|
951
|
+
<VStack>
|
|
939
952
|
<Layer1 />
|
|
940
953
|
<Layer2 />
|
|
941
|
-
</
|
|
954
|
+
</VStack>
|
|
942
955
|
|
|
943
956
|
// VTree — hierarchical tree view
|
|
944
|
-
<
|
|
957
|
+
<VTree nodes={treeData} />
|
|
945
958
|
// IVTreeNode type exported for tree node data structure
|
|
946
959
|
|
|
947
960
|
// VPhoto — background-image based photo with sizing
|
|
948
|
-
<
|
|
949
|
-
category={
|
|
950
|
-
position={
|
|
961
|
+
<VPhoto
|
|
962
|
+
category={PhotoCategory.Cover}
|
|
963
|
+
position={PhotoPositionType.Center}
|
|
951
964
|
src="/photo.jpg"
|
|
952
965
|
width="100%"
|
|
953
966
|
height="300px"
|
|
@@ -956,10 +969,10 @@ import IconSvg from './icon.svg';
|
|
|
956
969
|
// PhotoPositionType: Left, Center, Right
|
|
957
970
|
|
|
958
971
|
// VAvatar — user avatar display
|
|
959
|
-
<
|
|
972
|
+
<VAvatar ... />
|
|
960
973
|
|
|
961
974
|
// VDynamic — renders component by name from registry
|
|
962
|
-
<
|
|
975
|
+
<VDynamic
|
|
963
976
|
components={{ MyWidget: WidgetComponent, MyChart: ChartComponent }}
|
|
964
977
|
name="MyWidget"
|
|
965
978
|
customProp="value" // extra props are passed to the resolved component
|
|
@@ -971,39 +984,39 @@ import IconSvg from './icon.svg';
|
|
|
971
984
|
|
|
972
985
|
```tsx
|
|
973
986
|
// Debug utility component
|
|
974
|
-
<
|
|
987
|
+
<Debug ... />
|
|
975
988
|
|
|
976
989
|
// Google Font loader
|
|
977
|
-
<
|
|
978
|
-
type={
|
|
990
|
+
<VGoogleFont
|
|
991
|
+
type={GoogleFontType...}
|
|
979
992
|
...
|
|
980
993
|
/>
|
|
981
994
|
// GoogleFontType, IGoogleTypeface, IGoogleFont types available
|
|
982
995
|
|
|
983
996
|
// Chrome extension validation
|
|
984
|
-
<
|
|
997
|
+
<VChromeExtensionValidator ... />
|
|
985
998
|
// ExtensionStatus enum exported
|
|
986
999
|
|
|
987
1000
|
// Auth components
|
|
988
|
-
<
|
|
989
|
-
category={
|
|
990
|
-
uxMode={
|
|
1001
|
+
<GoogleGrant
|
|
1002
|
+
category={GoogleGrantCategory.Implicit}
|
|
1003
|
+
uxMode={GoogleGrantUXMode.Popup}
|
|
991
1004
|
...
|
|
992
1005
|
/>
|
|
993
1006
|
// GoogleGrantCategory: Implicit, AuthorizationCode
|
|
994
1007
|
// GoogleGrantUXMode: Popup, Redirect
|
|
995
1008
|
// GoogleGrantCode, GoogleGrantToken types exported
|
|
996
1009
|
|
|
997
|
-
<
|
|
998
|
-
uxMode={
|
|
1010
|
+
<GoogleSignin
|
|
1011
|
+
uxMode={GoogleSigninUXMode...}
|
|
999
1012
|
...
|
|
1000
1013
|
/>
|
|
1001
1014
|
|
|
1002
|
-
<
|
|
1003
|
-
<
|
|
1015
|
+
<FigmaSignin ... />
|
|
1016
|
+
<WeComSignin ... />
|
|
1004
1017
|
|
|
1005
1018
|
// JWT utility
|
|
1006
|
-
|
|
1019
|
+
parseJWT(token) // returns IUserProfile
|
|
1007
1020
|
// IUserProfile type exported
|
|
1008
1021
|
```
|
|
1009
1022
|
|
|
@@ -1011,34 +1024,34 @@ Den.Components.parseJWT(token) // returns IUserProfile
|
|
|
1011
1024
|
|
|
1012
1025
|
```tsx
|
|
1013
1026
|
// Semantic HTML wrappers
|
|
1014
|
-
<
|
|
1015
|
-
<
|
|
1016
|
-
<
|
|
1017
|
-
<
|
|
1018
|
-
<
|
|
1019
|
-
<
|
|
1020
|
-
<
|
|
1027
|
+
<VHeader>...</VHeader>
|
|
1028
|
+
<VNav>...</VNav>
|
|
1029
|
+
<VMain>...</VMain>
|
|
1030
|
+
<VAside>...</VAside>
|
|
1031
|
+
<VFooter>...</VFooter>
|
|
1032
|
+
<VSection>...</VSection>
|
|
1033
|
+
<VHR />
|
|
1021
1034
|
|
|
1022
1035
|
// Mask overlay
|
|
1023
|
-
<
|
|
1036
|
+
<VMask><Content /></VMask>
|
|
1024
1037
|
|
|
1025
1038
|
// Iframe
|
|
1026
|
-
<
|
|
1039
|
+
<VFrame src="https://example.com" />
|
|
1027
1040
|
|
|
1028
1041
|
// Loading indicator
|
|
1029
|
-
<
|
|
1042
|
+
<VLoading />
|
|
1030
1043
|
|
|
1031
1044
|
// Error boundary
|
|
1032
|
-
<
|
|
1045
|
+
<VErrorBoundary>
|
|
1033
1046
|
<RiskyComponent />
|
|
1034
|
-
</
|
|
1047
|
+
</VErrorBoundary>
|
|
1035
1048
|
```
|
|
1036
1049
|
|
|
1037
1050
|
### Utility Functions (core)
|
|
1038
1051
|
|
|
1039
1052
|
```tsx
|
|
1040
1053
|
// Convert FormData to nested JSON (used internally by VForm)
|
|
1041
|
-
|
|
1054
|
+
convertFormDataToJson(formData)
|
|
1042
1055
|
```
|
|
1043
1056
|
|
|
1044
1057
|
---
|
|
@@ -1050,9 +1063,9 @@ Den.Components.convertFormDataToJson(formData)
|
|
|
1050
1063
|
Before using network features, initialize the bootstrap:
|
|
1051
1064
|
|
|
1052
1065
|
```tsx
|
|
1053
|
-
import {
|
|
1066
|
+
import { ... } from '@fewbox/den-app';
|
|
1054
1067
|
|
|
1055
|
-
|
|
1068
|
+
boot({
|
|
1056
1069
|
getToken: () => localStorage.getItem('token') || '',
|
|
1057
1070
|
getAppSettings: () => ({
|
|
1058
1071
|
endpoint: {
|
|
@@ -1078,9 +1091,9 @@ Den.FewBox.boot({
|
|
|
1078
1091
|
});
|
|
1079
1092
|
|
|
1080
1093
|
// Or use the Boot component:
|
|
1081
|
-
<
|
|
1094
|
+
<Boot options={bootOptions} />
|
|
1082
1095
|
|
|
1083
|
-
// Types exported
|
|
1096
|
+
// Types exported:
|
|
1084
1097
|
// IFewBoxOptions, IAppSettings, IEndpoint, IError, BootClass
|
|
1085
1098
|
```
|
|
1086
1099
|
|
|
@@ -1089,21 +1102,21 @@ Den.FewBox.boot({
|
|
|
1089
1102
|
All Fetch clients return RxJS Observables for use in Redux Observable epics:
|
|
1090
1103
|
|
|
1091
1104
|
```tsx
|
|
1092
|
-
import {
|
|
1105
|
+
import { ... } from '@fewbox/den-app';
|
|
1093
1106
|
|
|
1094
1107
|
// Default endpoint (uses app settings endpoint)
|
|
1095
|
-
new
|
|
1096
|
-
new
|
|
1097
|
-
new
|
|
1098
|
-
new
|
|
1099
|
-
new
|
|
1108
|
+
new GetFetch('/users', (payload) => ({ type: 'USERS_LOADED', payload }));
|
|
1109
|
+
new PostFetch('/users', body, (payload) => ({ type: 'USER_CREATED', payload }));
|
|
1110
|
+
new PutFetch('/users/1', body, processAction);
|
|
1111
|
+
new PatchFetch('/users/1', body, processAction);
|
|
1112
|
+
new DeleteFetch('/users/1', processAction);
|
|
1100
1113
|
|
|
1101
1114
|
// External endpoint (bypasses app settings, uses named endpoint from appSettings)
|
|
1102
|
-
new
|
|
1103
|
-
new
|
|
1104
|
-
new
|
|
1105
|
-
new
|
|
1106
|
-
new
|
|
1115
|
+
new GetFetchExternal('externalService', '/data', processAction);
|
|
1116
|
+
new PostFetchExternal('externalService', '/data', body, processAction);
|
|
1117
|
+
new PutFetchExternal('externalService', '/data/1', body, processAction);
|
|
1118
|
+
new PatchFetchExternal('externalService', '/data/1', body, processAction);
|
|
1119
|
+
new DeleteFetchExternal('externalService', '/data/1', processAction);
|
|
1107
1120
|
```
|
|
1108
1121
|
|
|
1109
1122
|
### AJAX-based HTTP Clients
|
|
@@ -1112,43 +1125,43 @@ RxJS Ajax-based clients (alternative to Fetch):
|
|
|
1112
1125
|
|
|
1113
1126
|
```tsx
|
|
1114
1127
|
// Default endpoint
|
|
1115
|
-
new
|
|
1116
|
-
new
|
|
1117
|
-
new
|
|
1118
|
-
new
|
|
1119
|
-
new
|
|
1128
|
+
new Get('/users', processAction);
|
|
1129
|
+
new Post('/users', body, processAction);
|
|
1130
|
+
new Put('/users/1', body, processAction);
|
|
1131
|
+
new Patch('/users/1', body, processAction);
|
|
1132
|
+
new Delete('/users/1', processAction);
|
|
1120
1133
|
|
|
1121
1134
|
// External endpoint
|
|
1122
|
-
new
|
|
1123
|
-
new
|
|
1124
|
-
new
|
|
1125
|
-
new
|
|
1126
|
-
new
|
|
1135
|
+
new GetExternal('externalService', '/data', processAction);
|
|
1136
|
+
new PostExternal('externalService', '/data', body, processAction);
|
|
1137
|
+
new PutExternal('externalService', '/data/1', body, processAction);
|
|
1138
|
+
new PatchExternal('externalService', '/data/1', body, processAction);
|
|
1139
|
+
new DeleteExternal('externalService', '/data/1', processAction);
|
|
1127
1140
|
|
|
1128
1141
|
// Text response variants (returns text instead of JSON)
|
|
1129
|
-
new
|
|
1130
|
-
new
|
|
1131
|
-
new
|
|
1132
|
-
new
|
|
1133
|
-
new
|
|
1142
|
+
new GetText('/data', processAction);
|
|
1143
|
+
new PostText('/data', body, processAction);
|
|
1144
|
+
new PutText('/data', body, processAction);
|
|
1145
|
+
new PatchText('/data', body, processAction);
|
|
1146
|
+
new DeleteText('/data', processAction);
|
|
1134
1147
|
// External text variants: GetTextExternal, PostTextExternal, PutTextExternal, PatchTextExternal, DeleteTextExternal
|
|
1135
1148
|
|
|
1136
1149
|
// Local storage
|
|
1137
|
-
new
|
|
1150
|
+
new GetLocal('/local-path', processAction);
|
|
1138
1151
|
```
|
|
1139
1152
|
|
|
1140
1153
|
### GraphQL
|
|
1141
1154
|
|
|
1142
1155
|
```tsx
|
|
1143
1156
|
// Default endpoint
|
|
1144
|
-
new
|
|
1157
|
+
new GQL({ query: 'query { users { id name } }' }, processAction);
|
|
1145
1158
|
|
|
1146
1159
|
// External endpoint
|
|
1147
|
-
new
|
|
1160
|
+
new GQLExternal('externalService', { query: 'mutation { ... }' }, processAction);
|
|
1148
1161
|
|
|
1149
1162
|
// File upload
|
|
1150
|
-
new
|
|
1151
|
-
new
|
|
1163
|
+
new GQLUpload({ query: '...', variables: { file } }, processAction);
|
|
1164
|
+
new GQLExternalUpload('externalService', { query: '...' }, processAction);
|
|
1152
1165
|
|
|
1153
1166
|
// IGraphQL type available for query/mutation objects
|
|
1154
1167
|
```
|
|
@@ -1156,17 +1169,17 @@ new Den.Network.GQLExternalUpload('externalService', { query: '...' }, processAc
|
|
|
1156
1169
|
### Server-Sent Events (SSE)
|
|
1157
1170
|
|
|
1158
1171
|
```tsx
|
|
1159
|
-
new
|
|
1160
|
-
new
|
|
1161
|
-
new
|
|
1162
|
-
new
|
|
1172
|
+
new GetSSE('/events', processAction);
|
|
1173
|
+
new PostSSE('/events', processAction);
|
|
1174
|
+
new GetSSEExternal('externalService', '/events', processAction);
|
|
1175
|
+
new PostSSEExternal('externalService', '/events', processAction);
|
|
1163
1176
|
```
|
|
1164
1177
|
|
|
1165
1178
|
### WebSocket
|
|
1166
1179
|
|
|
1167
1180
|
```tsx
|
|
1168
|
-
new
|
|
1169
|
-
//
|
|
1181
|
+
new WS({ url: 'wss://example.com/ws' });
|
|
1182
|
+
// ws() function also available
|
|
1170
1183
|
// IWebsocketOptions type exported
|
|
1171
1184
|
```
|
|
1172
1185
|
|
|
@@ -1176,18 +1189,18 @@ For use outside Redux Observable epics:
|
|
|
1176
1189
|
|
|
1177
1190
|
```tsx
|
|
1178
1191
|
// Default endpoint
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1192
|
+
verbsGetPromise('/users');
|
|
1193
|
+
verbsPostPromise('/users', body);
|
|
1194
|
+
verbsPutPromise('/users/1', body);
|
|
1195
|
+
verbsPatchPromise('/users/1', body);
|
|
1196
|
+
verbsDeletePromise('/users/1');
|
|
1184
1197
|
|
|
1185
1198
|
// External endpoint
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1199
|
+
verbsGetPromiseExternal('externalService', '/data');
|
|
1200
|
+
verbsPostPromiseExternal('externalService', '/data', body);
|
|
1201
|
+
verbsPutPromiseExternal('externalService', '/data/1', body);
|
|
1202
|
+
verbsPatchPromiseExternal('externalService', '/data/1', body);
|
|
1203
|
+
verbsDeletePromiseExternal('externalService', '/data/1');
|
|
1191
1204
|
```
|
|
1192
1205
|
|
|
1193
1206
|
### Raw HTTP Verb Functions
|
|
@@ -1204,14 +1217,14 @@ Low-level verb functions that return Observables:
|
|
|
1204
1217
|
### Network Utilities
|
|
1205
1218
|
|
|
1206
1219
|
```tsx
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1220
|
+
buildHeaders(customHeaders); // Merge custom headers with JWT token
|
|
1221
|
+
buildUrl(path); // Build URL from app settings endpoint
|
|
1222
|
+
buildExternalUrl(externalName, path); // Build URL from named external endpoint
|
|
1223
|
+
JsonHeaders; // Default JSON content-type headers
|
|
1224
|
+
parseAjaxData(response); // Parse AJAX response
|
|
1225
|
+
parseFetchData(response); // Parse Fetch response
|
|
1226
|
+
parseGQLAjaxData(response); // Parse GraphQL response
|
|
1227
|
+
convertJsonToGraphQLSchema(json); // Convert JSON to GraphQL schema string
|
|
1215
1228
|
```
|
|
1216
1229
|
|
|
1217
1230
|
### Redux Observable Epic Pattern
|
|
@@ -1224,7 +1237,7 @@ const fetchUsersEpic = (action$) =>
|
|
|
1224
1237
|
action$.pipe(
|
|
1225
1238
|
ofType('FETCH_USERS'),
|
|
1226
1239
|
switchMap(() =>
|
|
1227
|
-
new
|
|
1240
|
+
new GetFetch('/users', (payload) => ({
|
|
1228
1241
|
type: 'USERS_LOADED',
|
|
1229
1242
|
payload
|
|
1230
1243
|
}))
|
|
@@ -1245,20 +1258,20 @@ const fetchUsersEpic = (action$) =>
|
|
|
1245
1258
|
Type definitions for Redux store integration:
|
|
1246
1259
|
|
|
1247
1260
|
```tsx
|
|
1248
|
-
import {
|
|
1261
|
+
import { ... } from '@fewbox/den-app';
|
|
1249
1262
|
|
|
1250
1263
|
// Pagination
|
|
1251
|
-
type IList =
|
|
1252
|
-
type IPaging =
|
|
1253
|
-
type IContinuePaging =
|
|
1264
|
+
type IList = IList; // List response wrapper
|
|
1265
|
+
type IPaging = IPaging; // Paged list with page/size
|
|
1266
|
+
type IContinuePaging = IContinuePaging; // Cursor-based pagination
|
|
1254
1267
|
|
|
1255
1268
|
// Data types
|
|
1256
|
-
type Json =
|
|
1257
|
-
type EnumDictionary<K, V> =
|
|
1269
|
+
type Json = Json; // Generic JSON type
|
|
1270
|
+
type EnumDictionary<K, V> = EnumDictionary<K, V>; // Enum-keyed dictionary
|
|
1258
1271
|
|
|
1259
1272
|
// Response wrappers
|
|
1260
|
-
type IMetaResponse =
|
|
1261
|
-
type IPayloadResponse =
|
|
1273
|
+
type IMetaResponse = IMetaResponse; // Response with metadata
|
|
1274
|
+
type IPayloadResponse = IPayloadResponse; // Response with payload
|
|
1262
1275
|
```
|
|
1263
1276
|
|
|
1264
1277
|
---
|
|
@@ -1268,18 +1281,18 @@ type IPayloadResponse = Den.Store.IPayloadResponse; // Response with payload
|
|
|
1268
1281
|
Internationalization utilities:
|
|
1269
1282
|
|
|
1270
1283
|
```tsx
|
|
1271
|
-
import {
|
|
1284
|
+
import { ... } from '@fewbox/den-app';
|
|
1272
1285
|
|
|
1273
1286
|
// Get browser language
|
|
1274
|
-
|
|
1275
|
-
|
|
1287
|
+
getBrowserLang(); // "en" (short code)
|
|
1288
|
+
getBrowserLangFull(); // "en-US" (full code)
|
|
1276
1289
|
|
|
1277
1290
|
// Get formatted intl message (uses BootClass language config)
|
|
1278
|
-
|
|
1291
|
+
getIntlMessage('Hello.World', 'en', { name: 'World' });
|
|
1279
1292
|
|
|
1280
1293
|
// Select messages for a locale from a locales object
|
|
1281
|
-
const messages =
|
|
1282
|
-
const messagesFull =
|
|
1294
|
+
const messages = language(locales, 'en');
|
|
1295
|
+
const messagesFull = languageFull(locales, 'en-US');
|
|
1283
1296
|
```
|
|
1284
1297
|
|
|
1285
1298
|
---
|
|
@@ -1320,114 +1333,114 @@ RightTop, RightBottom, LeftTop, LeftBottom, TopBottom, LeftRight
|
|
|
1320
1333
|
### Page Layout (header + content + footer)
|
|
1321
1334
|
|
|
1322
1335
|
```tsx
|
|
1323
|
-
<
|
|
1324
|
-
<
|
|
1336
|
+
<Y height="100vh">
|
|
1337
|
+
<VBoundary padding="1em" backgroundColor={ColorType.Primary}>
|
|
1325
1338
|
<Header />
|
|
1326
|
-
</
|
|
1327
|
-
<
|
|
1339
|
+
</VBoundary>
|
|
1340
|
+
<VBoundary selfGrow={1} overflow="auto" padding="1em">
|
|
1328
1341
|
<MainContent />
|
|
1329
|
-
</
|
|
1330
|
-
<
|
|
1342
|
+
</VBoundary>
|
|
1343
|
+
<VBoundary padding="1em" backgroundColor={ColorType.Dark}>
|
|
1331
1344
|
<Footer />
|
|
1332
|
-
</
|
|
1333
|
-
</
|
|
1345
|
+
</VBoundary>
|
|
1346
|
+
</Y>
|
|
1334
1347
|
```
|
|
1335
1348
|
|
|
1336
1349
|
### Centered Content
|
|
1337
1350
|
|
|
1338
1351
|
```tsx
|
|
1339
|
-
<
|
|
1340
|
-
<
|
|
1341
|
-
</
|
|
1352
|
+
<XCenter height="100%">
|
|
1353
|
+
<SCenter><Content /></SCenter>
|
|
1354
|
+
</XCenter>
|
|
1342
1355
|
```
|
|
1343
1356
|
|
|
1344
1357
|
### Sidebar Layout
|
|
1345
1358
|
|
|
1346
1359
|
```tsx
|
|
1347
|
-
<
|
|
1348
|
-
<
|
|
1360
|
+
<X height="100vh">
|
|
1361
|
+
<VBoundary width="250px" backgroundColor={ColorType.Dark}>
|
|
1349
1362
|
<Sidebar />
|
|
1350
|
-
</
|
|
1351
|
-
<
|
|
1352
|
-
</
|
|
1363
|
+
</VBoundary>
|
|
1364
|
+
<VBoundary selfGrow={1}><MainContent /></VBoundary>
|
|
1365
|
+
</X>
|
|
1353
1366
|
```
|
|
1354
1367
|
|
|
1355
1368
|
### Sticky Navigation Bar
|
|
1356
1369
|
|
|
1357
1370
|
```tsx
|
|
1358
|
-
<
|
|
1359
|
-
<
|
|
1360
|
-
<
|
|
1361
|
-
<
|
|
1362
|
-
<
|
|
1363
|
-
<
|
|
1364
|
-
</
|
|
1365
|
-
</
|
|
1366
|
-
</
|
|
1371
|
+
<Position category={PositionCategory.Edge} top="0px" isFullWidth zIndex={10}>
|
|
1372
|
+
<XBetween height="48px" padding="0 1em" backgroundColor={ColorType.Primary}>
|
|
1373
|
+
<VLabel caption="Logo" frontColor={ColorType.White} />
|
|
1374
|
+
<XRight gap="1em">
|
|
1375
|
+
<VHyperlink category={HyperlinkCategory.Self} to="/about">About</VHyperlink>
|
|
1376
|
+
<VHyperlink category={HyperlinkCategory.Self} to="/contact">Contact</VHyperlink>
|
|
1377
|
+
</XRight>
|
|
1378
|
+
</XBetween>
|
|
1379
|
+
</Position>
|
|
1367
1380
|
```
|
|
1368
1381
|
|
|
1369
1382
|
### Form with Validation
|
|
1370
1383
|
|
|
1371
1384
|
```tsx
|
|
1372
|
-
<
|
|
1385
|
+
<VForm
|
|
1373
1386
|
handleSubmit={(data) => api.login(data.form)}
|
|
1374
1387
|
handleValidateError={(invalidInputs) => { invalidInputs[0]?.focus(); }}>
|
|
1375
|
-
<
|
|
1376
|
-
<
|
|
1377
|
-
<
|
|
1378
|
-
<
|
|
1379
|
-
frontColor={
|
|
1380
|
-
</
|
|
1381
|
-
</
|
|
1388
|
+
<Y gap="1em" padding="2em">
|
|
1389
|
+
<VEmail name="form.email" required placeholder="Email" label="Email" />
|
|
1390
|
+
<VPassword name="form.password" required minLength={8} placeholder="Password" label="Password" />
|
|
1391
|
+
<VSubmit caption="Sign In" backgroundColor={ColorType.Primary}
|
|
1392
|
+
frontColor={ColorType.White} borderRadius="0.5em" padding="0.8em 2em" />
|
|
1393
|
+
</Y>
|
|
1394
|
+
</VForm>
|
|
1382
1395
|
```
|
|
1383
1396
|
|
|
1384
1397
|
### Card Grid (app variant)
|
|
1385
1398
|
|
|
1386
1399
|
```tsx
|
|
1387
|
-
<
|
|
1400
|
+
<XWrapEvenly gap="1em" padding="1em">
|
|
1388
1401
|
{items.map(item => (
|
|
1389
|
-
<
|
|
1402
|
+
<VCardMedia
|
|
1390
1403
|
key={item.id}
|
|
1391
1404
|
width="300px"
|
|
1392
1405
|
borderRadius="0.5em"
|
|
1393
|
-
borderColor={
|
|
1406
|
+
borderColor={ColorType.Border}
|
|
1394
1407
|
borderStyle="solid"
|
|
1395
1408
|
borderWidth="1px"
|
|
1396
|
-
media={<
|
|
1397
|
-
head={<
|
|
1398
|
-
body={<
|
|
1409
|
+
media={<VImage src={item.image} category={ImageCategory.FullSize} />}
|
|
1410
|
+
head={<VLabel category={LabelCategory.H3} caption={item.title} />}
|
|
1411
|
+
body={<VText caption={item.description} />}
|
|
1399
1412
|
/>
|
|
1400
1413
|
))}
|
|
1401
|
-
</
|
|
1414
|
+
</XWrapEvenly>
|
|
1402
1415
|
```
|
|
1403
1416
|
|
|
1404
1417
|
### Absolute Positioning Inside Container
|
|
1405
1418
|
|
|
1406
1419
|
```tsx
|
|
1407
|
-
<
|
|
1408
|
-
<
|
|
1409
|
-
<
|
|
1420
|
+
<PositionArea category={PositionAreaCategory.FullSize}>
|
|
1421
|
+
<VBoundary height="300px" backgroundColor={ColorType.Dark}>
|
|
1422
|
+
<Position category={PositionCategory.Area} type={PositionType.LeftTop}>
|
|
1410
1423
|
<TopLeftContent />
|
|
1411
|
-
</
|
|
1412
|
-
<
|
|
1424
|
+
</Position>
|
|
1425
|
+
<Position category={PositionCategory.Area} type={PositionType.Center}>
|
|
1413
1426
|
<CenterContent />
|
|
1414
|
-
</
|
|
1415
|
-
</
|
|
1416
|
-
</
|
|
1427
|
+
</Position>
|
|
1428
|
+
</VBoundary>
|
|
1429
|
+
</PositionArea>
|
|
1417
1430
|
```
|
|
1418
1431
|
|
|
1419
1432
|
### Responsive Layout
|
|
1420
1433
|
|
|
1421
1434
|
```tsx
|
|
1422
|
-
<
|
|
1423
|
-
breakpointType={
|
|
1424
|
-
desktop={<
|
|
1435
|
+
<Responsive
|
|
1436
|
+
breakpointType={ScreenSizeType.Medium}
|
|
1437
|
+
desktop={<X gap="1em">
|
|
1425
1438
|
<Sidebar />
|
|
1426
1439
|
<MainContent />
|
|
1427
|
-
</
|
|
1428
|
-
mobile={<
|
|
1440
|
+
</X>}
|
|
1441
|
+
mobile={<Y gap="1em">
|
|
1429
1442
|
<MainContent />
|
|
1430
|
-
</
|
|
1443
|
+
</Y>}
|
|
1431
1444
|
/>
|
|
1432
1445
|
```
|
|
1433
1446
|
|