@fewbox/den-web 0.2.0-preview.44 → 0.2.0-preview.45
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/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/copy-templates.js +12 -4
- package/src/components/View/VInput/VCheckBox/index.d.ts +4 -0
- package/src/components/View/VInput/VRadio/index.d.ts +4 -0
- package/src/components/View/VSwitch/index.d.ts +8 -1
- package/templates/.claude/skills/fewbox-den/SKILL.md +84 -2
- package/tsconfig.app.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -2,7 +2,9 @@ import { fileURLToPath } from 'url';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const OVERWRITE_LIST = [path.join('.claude', 'skills') + path.sep];
|
|
6
|
+
|
|
7
|
+
const copyFolderAsync = (source, target, rootSource) => {
|
|
6
8
|
fs.mkdirSync(target, { recursive: true });
|
|
7
9
|
const entries = fs.readdirSync(source, { withFileTypes: true });
|
|
8
10
|
for (let entry of entries) {
|
|
@@ -10,11 +12,17 @@ const copyFolderAsync = (source, target) => {
|
|
|
10
12
|
const destPath = path.join(target, entry.name);
|
|
11
13
|
|
|
12
14
|
if (entry.isDirectory()) {
|
|
13
|
-
copyFolderAsync(srcPath, destPath);
|
|
15
|
+
copyFolderAsync(srcPath, destPath, rootSource);
|
|
14
16
|
} else {
|
|
15
|
-
|
|
17
|
+
const rel = path.relative(rootSource, srcPath);
|
|
18
|
+
const forceOverwrite = OVERWRITE_LIST.some(prefix => rel.startsWith(prefix));
|
|
19
|
+
const exists = fs.existsSync(destPath);
|
|
20
|
+
if (exists && !forceOverwrite) {
|
|
16
21
|
console.log(`You should merge '${destPath}' manually!`);
|
|
17
22
|
} else {
|
|
23
|
+
if (exists) {
|
|
24
|
+
console.log(`Overwriting '${destPath}' with latest version.`);
|
|
25
|
+
}
|
|
18
26
|
fs.copyFileSync(srcPath, destPath);
|
|
19
27
|
}
|
|
20
28
|
}
|
|
@@ -26,4 +34,4 @@ const rootdir = path.dirname(__filename);
|
|
|
26
34
|
const projectRoot = process.env.INIT_CWD || process.cwd();
|
|
27
35
|
const templateDir = path.join(rootdir, '../templates');
|
|
28
36
|
|
|
29
|
-
copyFolderAsync(templateDir, projectRoot);
|
|
37
|
+
copyFolderAsync(templateDir, projectRoot, templateDir);
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { Property } from 'csstype';
|
|
1
2
|
import { IBaseInputProps } from "..";
|
|
2
3
|
export interface IVCheckBoxProps extends IBaseInputProps {
|
|
3
4
|
isChecked?: boolean;
|
|
5
|
+
checkedSvg?: React.ReactNode;
|
|
6
|
+
uncheckedSvg?: React.ReactNode;
|
|
7
|
+
svgSize?: Property.Height;
|
|
4
8
|
}
|
|
5
9
|
declare const VCheckBox: (props: IVCheckBoxProps) => React.JSX.Element;
|
|
6
10
|
export default VCheckBox;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { Property } from 'csstype';
|
|
1
2
|
import { IBaseInputProps } from "..";
|
|
2
3
|
export interface IVRadioProps extends IBaseInputProps {
|
|
3
4
|
isChecked?: boolean;
|
|
5
|
+
checkedSvg?: React.ReactNode;
|
|
6
|
+
uncheckedSvg?: React.ReactNode;
|
|
7
|
+
svgSize?: Property.Height;
|
|
4
8
|
}
|
|
5
9
|
declare const VRadio: (props: IVRadioProps) => React.JSX.Element;
|
|
6
10
|
export default VRadio;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { Property } from 'csstype';
|
|
2
|
+
import { FullGapType, FullPaddingType } from '../../Engine';
|
|
1
3
|
import { IBaseProps, IBaseStates } from '../../Engine/Base';
|
|
2
4
|
export interface IVSwitchProps extends IBaseProps {
|
|
3
5
|
isOn: boolean;
|
|
4
6
|
icon?: React.JSX.Element;
|
|
5
|
-
|
|
7
|
+
onIcon?: React.JSX.Element;
|
|
8
|
+
offIcon?: React.JSX.Element;
|
|
9
|
+
thumbSvg?: React.ReactNode;
|
|
10
|
+
thumbSize?: Property.Height;
|
|
11
|
+
trackPadding?: FullPaddingType;
|
|
12
|
+
gap?: FullGapType;
|
|
6
13
|
isDisabled?: boolean;
|
|
7
14
|
onSwitch?: (isOn: boolean) => void;
|
|
8
15
|
}
|
|
@@ -64,7 +64,7 @@ All components extend `IBaseProps` (which extends `IViewProps`) providing these
|
|
|
64
64
|
| `zIndex` | `Property.ZIndex` | CSS z-index |
|
|
65
65
|
| `fontFamily` | `Property.FontFamily` | CSS font-family |
|
|
66
66
|
| `letterSpacing` | `LetterSpacingType \| Property.LetterSpacing` | Letter spacing preset or custom CSS value |
|
|
67
|
-
| `lineHeight` | `Property.LineHeight` |
|
|
67
|
+
| `lineHeight` | `LineHeightType \| Property.LineHeight` | Line height preset or custom CSS value |
|
|
68
68
|
| `selfShrink` | `number` | flex-shrink for the component itself |
|
|
69
69
|
| `selfGrow` | `number` | flex-grow for the component itself |
|
|
70
70
|
| `selfBasis` | `string` | flex-basis for the component itself |
|
|
@@ -165,6 +165,14 @@ GapType.Default | .Normal | .ExtraSmall | .Small | .Large | .ExtraLarge
|
|
|
165
165
|
// Letter Spacing
|
|
166
166
|
LetterSpacingType.Default | .Normal | .ExtraSmall | .Small | .Large | .ExtraLarge
|
|
167
167
|
|
|
168
|
+
// Line Height (Apple HIG-inspired scale)
|
|
169
|
+
LineHeightType.Default // inherits
|
|
170
|
+
LineHeightType.Normal // 1.0
|
|
171
|
+
LineHeightType.ExtraSmall // 1.15
|
|
172
|
+
LineHeightType.Small // 1.25
|
|
173
|
+
LineHeightType.Large // 1.35
|
|
174
|
+
LineHeightType.ExtraLarge // 1.45
|
|
175
|
+
|
|
168
176
|
// Opacity
|
|
169
177
|
OpacityType._0 | ._10 | ._20 | ._25 | ._30 | ._40 | ._50 | ._60 | ._70 | ._75 | ._80 | ._90 | ._100
|
|
170
178
|
|
|
@@ -495,6 +503,10 @@ Wrap a child in an S-component to override the parent's cross-axis alignment for
|
|
|
495
503
|
|
|
496
504
|
// FlexItem also exports FlexItemAlignSelfType
|
|
497
505
|
// Flex also exports FlexAlignContentType
|
|
506
|
+
|
|
507
|
+
// Note: Flex does NOT set overflow by default (as of 2026-03).
|
|
508
|
+
// Children can overflow the flex container unless you set overflow explicitly.
|
|
509
|
+
// Pass `overflow={OverflowType.Hidden}` if you want clipping.
|
|
498
510
|
```
|
|
499
511
|
|
|
500
512
|
---
|
|
@@ -698,6 +710,16 @@ const [value, setValue] = useState('');
|
|
|
698
710
|
)}
|
|
699
711
|
/>
|
|
700
712
|
|
|
713
|
+
// Input-scoped color props (apply to the native <input> only, not the wrapper):
|
|
714
|
+
<VTextBox
|
|
715
|
+
name="form.email"
|
|
716
|
+
frontColor={ColorType.Dark} // wrapper text color; also applied to <input> if inputFrontColor is omitted
|
|
717
|
+
inputFrontColor={ColorType.Black} // <input> text color (overrides frontColor on input)
|
|
718
|
+
inputBackgroundColor={ColorType.White} // <input> background color
|
|
719
|
+
placeholderColor={ColorType.Placeholder} // placeholder text color
|
|
720
|
+
/>
|
|
721
|
+
// Available on all VInput-based components (VTextBox, VEmail, VPassword, VNumber, VDate, VTextArea, VDropdown, etc.)
|
|
722
|
+
|
|
701
723
|
// Other input types:
|
|
702
724
|
<VEmail name="form.email" required placeholder="Email" />
|
|
703
725
|
<VPassword name="form.password" placeholder="Password" />
|
|
@@ -725,10 +747,34 @@ const [value, setValue] = useState('');
|
|
|
725
747
|
frontColor={ColorType.Primary}
|
|
726
748
|
/>
|
|
727
749
|
|
|
750
|
+
// Checkbox with custom SVG icons and size override.
|
|
751
|
+
// checkedSvg/uncheckedSvg replace the built-in checkbox icons.
|
|
752
|
+
// svgSize sets the inline height on VSvg, overriding --den-v-svg-size (default 2.2em).
|
|
753
|
+
<VCheckBox
|
|
754
|
+
name="form.liked"
|
|
755
|
+
label="Like"
|
|
756
|
+
isChecked={liked}
|
|
757
|
+
checkedSvg={<LikeFilledSvg />}
|
|
758
|
+
uncheckedSvg={<LikeOutlineSvg />}
|
|
759
|
+
svgSize="1.2em"
|
|
760
|
+
frontColor={ColorType.Error}
|
|
761
|
+
/>
|
|
762
|
+
|
|
728
763
|
// Radio
|
|
729
764
|
<VRadio id="opt1" name="form.option" value="A" label="Option A" />
|
|
730
765
|
<VRadio id="opt2" name="form.option" value="B" label="Option B" />
|
|
731
766
|
|
|
767
|
+
// Radio also accepts checkedSvg / uncheckedSvg / svgSize (same API as VCheckBox).
|
|
768
|
+
<VRadio
|
|
769
|
+
name="form.option"
|
|
770
|
+
value="A"
|
|
771
|
+
label="Option A"
|
|
772
|
+
isChecked={selected === 'A'}
|
|
773
|
+
checkedSvg={<StarFilledSvg />}
|
|
774
|
+
uncheckedSvg={<StarOutlineSvg />}
|
|
775
|
+
svgSize="1.2em"
|
|
776
|
+
/>
|
|
777
|
+
|
|
732
778
|
// Radio Group (managed state)
|
|
733
779
|
<VGroup
|
|
734
780
|
selectedValue="A"
|
|
@@ -772,6 +818,7 @@ const [value, setValue] = useState('');
|
|
|
772
818
|
menuGap="4px" // gap between dropdown menu and trigger
|
|
773
819
|
menuBackgroundColor={ColorType.White}
|
|
774
820
|
menuZIndex={9999}
|
|
821
|
+
menuMaxHeight="300px" // scrollable menu when items exceed height
|
|
775
822
|
handleChange={(value) => console.log(value)}
|
|
776
823
|
overWriteDropdownMenu={(items) => <CustomMenu items={items} />} // custom menu renderer
|
|
777
824
|
overWriteDropdownItem={(item) => <CustomItem item={item} />} // custom item renderer
|
|
@@ -781,10 +828,28 @@ const [value, setValue] = useState('');
|
|
|
781
828
|
// Switch
|
|
782
829
|
<VSwitch
|
|
783
830
|
isOn={true}
|
|
784
|
-
icon={<SwitchIcon />}
|
|
831
|
+
icon={<SwitchIcon />} // shown on the empty side in BOTH states (legacy single-icon prop)
|
|
785
832
|
onSwitch={(isOn) => console.log(isOn)}
|
|
786
833
|
/>
|
|
787
834
|
|
|
835
|
+
// Switch with state-specific icons, custom thumb svg, custom thumb size, and track padding.
|
|
836
|
+
// onIcon shows on the right when isOn=true, offIcon on the left when isOn=false.
|
|
837
|
+
// onIcon/offIcon override `icon` when provided.
|
|
838
|
+
// thumbSvg replaces the built-in switch knob graphic.
|
|
839
|
+
// thumbSize sets the inline height on the thumb VSvg, overriding --den-v-svg-size.
|
|
840
|
+
// trackPadding controls inner spacing between SVGs and the colored track edge
|
|
841
|
+
// (default '0.2em'). Applied symmetrically — set to '0' for flush, larger values for roomier look.
|
|
842
|
+
<VSwitch
|
|
843
|
+
isOn={isOn}
|
|
844
|
+
onSwitch={setIsOn}
|
|
845
|
+
onIcon={<SunSvg />}
|
|
846
|
+
offIcon={<MoonSvg />}
|
|
847
|
+
thumbSvg={<StarSvg />}
|
|
848
|
+
thumbSize="1.5em"
|
|
849
|
+
trackPadding="0.4em"
|
|
850
|
+
frontColor={ColorType.Success}
|
|
851
|
+
/>
|
|
852
|
+
|
|
788
853
|
// File upload
|
|
789
854
|
<VFile
|
|
790
855
|
name="form.avatar"
|
|
@@ -792,6 +857,23 @@ const [value, setValue] = useState('');
|
|
|
792
857
|
accept=".jpg,.png"
|
|
793
858
|
appearance={<UploadIcon />} // Custom trigger element
|
|
794
859
|
/>
|
|
860
|
+
|
|
861
|
+
// Custom preview (sync or async) — e.g., render a cached image gallery.
|
|
862
|
+
// `reload` re-triggers renderPreview (e.g., after cache refresh).
|
|
863
|
+
<VFile
|
|
864
|
+
name="form.photos"
|
|
865
|
+
multiple
|
|
866
|
+
renderPreview={(inputFiles, isLoading, reload) => (
|
|
867
|
+
<XWrapEvenly gap="0.5em">
|
|
868
|
+
{inputFiles.map((f, i) => (
|
|
869
|
+
<VImage key={i} src={f.objectUrl} category={ImageCategory.Squared} width="80px" />
|
|
870
|
+
))}
|
|
871
|
+
</XWrapEvenly>
|
|
872
|
+
)}
|
|
873
|
+
/>
|
|
874
|
+
// renderPreview signature: (inputFiles: InputFile[], isLoading: boolean, reload: () => void) => JSX.Element | Promise<JSX.Element>
|
|
875
|
+
// InputFile: { file: File; objectUrl?: string; ... }
|
|
876
|
+
// Async renderPreview shows `appearance` as fallback until the promise resolves.
|
|
795
877
|
// FileCategory enum available for file categories
|
|
796
878
|
|
|
797
879
|
// File with base64 output
|