poseui 0.0.2 → 0.0.4
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 +27 -6
- package/dist/index.js +61 -11
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,9 @@ type Child<TProps> = ChildValue | ((props: TProps) => ChildValue);
|
|
|
46
46
|
type RenderReturn<TSchema extends StandardSchemaV1 | undefined> = TSchema extends StandardSchemaV1 ? ReturnType<TSchema["~standard"]["validate"]> extends Promise<any> ? Promise<string> : string : string;
|
|
47
47
|
type CallArgs<TProps extends Record<string, unknown>, TSchema> = TSchema extends StandardSchemaV1 ? [TProps?] : [keyof TProps] extends [never] ? [TProps?] : [TProps];
|
|
48
48
|
type ClassEntry<TProps> = string | ((props: TProps) => string);
|
|
49
|
+
/** null means the attribute is omitted from the rendered output */
|
|
50
|
+
type AttrValue = string | null;
|
|
51
|
+
type AttrRecord<TProps> = Record<string, Dyn<TProps, AttrValue>>;
|
|
49
52
|
interface PoseElement<TProps extends Record<string, unknown>, TSchema extends StandardSchemaV1 | undefined = undefined> {
|
|
50
53
|
(...args: CallArgs<TProps, TSchema>): RenderReturn<TSchema>;
|
|
51
54
|
readonly classes: ReadonlyArray<ClassEntry<TProps>>;
|
|
@@ -782,22 +785,40 @@ interface PoseElement<TProps extends Record<string, unknown>, TSchema extends St
|
|
|
782
785
|
* Apply styles conditionally — predicate form or value-switch form.
|
|
783
786
|
*
|
|
784
787
|
* Predicate form (apply when true):
|
|
785
|
-
*
|
|
788
|
+
* \`\`\`ts
|
|
786
789
|
* .when(({ disabled }) => disabled, (b) => b.opacity(50).cursor_not_allowed())
|
|
787
|
-
*
|
|
790
|
+
* \`\`\`
|
|
788
791
|
*
|
|
789
792
|
* Value form (switch on a prop key):
|
|
790
|
-
*
|
|
793
|
+
* \`\`\`ts
|
|
791
794
|
* .when("variant", {
|
|
792
795
|
* primary: (b) => b.bg("indigo-600").text_color("white"),
|
|
793
796
|
* secondary: (b) => b.bg("slate-200").text_color("slate-900"),
|
|
794
797
|
* })
|
|
795
|
-
*
|
|
798
|
+
* \`\`\`
|
|
796
799
|
* Cases are Partial — unmatched values emit no classes.
|
|
797
800
|
* Multiple .when() calls are independent and all evaluated at render time.
|
|
798
801
|
*/
|
|
799
802
|
when(pred: (props: TProps) => boolean, apply: (b: PoseElement<TProps, undefined>) => PoseElement<TProps, any>): PoseElement<TProps, TSchema>;
|
|
800
803
|
when<K extends keyof TProps>(key: K, cases: Partial<Record<TProps[K] & PropertyKey, (b: PoseElement<TProps, undefined>) => PoseElement<TProps, any>>>): PoseElement<TProps, TSchema>;
|
|
804
|
+
/**
|
|
805
|
+
* Set a single HTML attribute. Value can be static or derived from props.
|
|
806
|
+
* Pass `null` to omit the attribute.
|
|
807
|
+
*
|
|
808
|
+
* @example
|
|
809
|
+
* pose.as('a').attr('href', ({ url }) => url).attr('target', '_blank')
|
|
810
|
+
*/
|
|
811
|
+
attr(name: string, value: Dyn<TProps, AttrValue>): PoseElement<TProps, TSchema>;
|
|
812
|
+
/**
|
|
813
|
+
* Set multiple HTML attributes at once. Each value can be static or a
|
|
814
|
+
* `(props) => string | null` function. `null` omits the attribute.
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* pose.as('input').attrs({ type: 'text', name: ({ field }) => field, required: ({ req }) => req ? '' : null })
|
|
818
|
+
* // or with a props function for multi-field logic:
|
|
819
|
+
* pose.as('a').attrs(({ url, external }) => ({ href: url, target: external ? '_blank' : null }))
|
|
820
|
+
*/
|
|
821
|
+
attrs(record: AttrRecord<TProps> | ((props: TProps) => Record<string, AttrValue>)): PoseElement<TProps, TSchema>;
|
|
801
822
|
/**
|
|
802
823
|
* Append any raw Tailwind class — static or derived from props.
|
|
803
824
|
* @example
|
|
@@ -808,7 +829,7 @@ interface PoseElement<TProps extends Record<string, unknown>, TSchema extends St
|
|
|
808
829
|
child(fn: (props: TProps) => ChildValue): PoseElement<TProps, TSchema>;
|
|
809
830
|
child(value: ChildValue): PoseElement<TProps, TSchema>;
|
|
810
831
|
/**
|
|
811
|
-
* Render to
|
|
832
|
+
* Render to \`{ html, css }\` using UnoCSS + presetWind4.
|
|
812
833
|
* @example
|
|
813
834
|
* const { html, css } = await card.render({ name: 'Ada' })
|
|
814
835
|
*/
|
|
@@ -825,4 +846,4 @@ interface Pose {
|
|
|
825
846
|
declare const pose: Pose;
|
|
826
847
|
declare const div: PoseElement<Record<never, never>, undefined>;
|
|
827
848
|
//#endregion
|
|
828
|
-
export { Child, ChildValue, Dyn, Pose, PoseElement, PoseValidationError, StandardSchemaV1, pose as default, div };
|
|
849
|
+
export { AttrRecord, AttrValue, Child, ChildValue, Dyn, Pose, PoseElement, PoseValidationError, StandardSchemaV1, pose as default, div };
|
package/dist/index.js
CHANGED
|
@@ -29,26 +29,47 @@ function arbitrary(value) {
|
|
|
29
29
|
function resolveClasses(classes, props) {
|
|
30
30
|
return classes.map((c) => typeof c === "function" ? c(props) : c).filter(Boolean).join(" ");
|
|
31
31
|
}
|
|
32
|
+
function renderAttrPair(name, value) {
|
|
33
|
+
if (value === null) return "";
|
|
34
|
+
return value === "" ? name : `${name}="${value}"`;
|
|
35
|
+
}
|
|
36
|
+
function resolveAttrs(attrs, props) {
|
|
37
|
+
const parts = [];
|
|
38
|
+
for (const entry of attrs) if (entry[0] === "single") {
|
|
39
|
+
const [, name, value] = entry;
|
|
40
|
+
const rendered = renderAttrPair(name, typeof value === "function" ? value(props) : value);
|
|
41
|
+
if (rendered) parts.push(rendered);
|
|
42
|
+
} else {
|
|
43
|
+
const [, fn] = entry;
|
|
44
|
+
for (const [name, value] of Object.entries(fn(props))) {
|
|
45
|
+
const rendered = renderAttrPair(name, value);
|
|
46
|
+
if (rendered) parts.push(rendered);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return parts.join(" ");
|
|
50
|
+
}
|
|
32
51
|
function renderChild(child, props) {
|
|
33
52
|
if (typeof child === "function") return renderChild(child(props), props);
|
|
34
53
|
if (Array.isArray(child)) return child.filter((c) => c != null).map((c) => renderChild(c, props)).join("");
|
|
35
54
|
if (child != null && typeof child === "object" && "__pose" in child) return child(props);
|
|
36
55
|
return child == null ? "" : String(child);
|
|
37
56
|
}
|
|
38
|
-
/** A tagless builder used inside .when() callbacks — only accumulates classes. */
|
|
57
|
+
/** A tagless builder used inside .when() callbacks — only accumulates classes and children. */
|
|
39
58
|
function createBlankBuilder() {
|
|
40
59
|
return createBuilder({
|
|
41
60
|
tag: "div",
|
|
42
61
|
classes: [],
|
|
62
|
+
attrs: [],
|
|
43
63
|
children: [],
|
|
44
64
|
schema: void 0
|
|
45
65
|
});
|
|
46
66
|
}
|
|
47
67
|
function createBuilder(state) {
|
|
48
|
-
function derive(extraClasses = [], extraChildren = []) {
|
|
68
|
+
function derive(extraClasses = [], extraChildren = [], extraAttrs = []) {
|
|
49
69
|
return createBuilder({
|
|
50
70
|
...state,
|
|
51
71
|
classes: [...state.classes, ...extraClasses],
|
|
72
|
+
attrs: [...state.attrs, ...extraAttrs],
|
|
52
73
|
children: [...state.children, ...extraChildren]
|
|
53
74
|
});
|
|
54
75
|
}
|
|
@@ -64,9 +85,11 @@ function createBuilder(state) {
|
|
|
64
85
|
}
|
|
65
86
|
function buildHtml(resolvedProps) {
|
|
66
87
|
const classStr = resolveClasses(state.classes, resolvedProps);
|
|
88
|
+
const attrsStr = resolveAttrs(state.attrs, resolvedProps);
|
|
67
89
|
const childrenStr = state.children.map((c) => renderChild(c, resolvedProps)).join("");
|
|
68
90
|
const classAttr = classStr ? ` class="${classStr}"` : "";
|
|
69
|
-
|
|
91
|
+
const attrsAttr = attrsStr ? ` ${attrsStr}` : "";
|
|
92
|
+
return `<${state.tag}${classAttr}${attrsAttr}>${childrenStr}</${state.tag}>`;
|
|
70
93
|
}
|
|
71
94
|
function render(...args) {
|
|
72
95
|
const props = args[0] ?? {};
|
|
@@ -76,6 +99,7 @@ function createBuilder(state) {
|
|
|
76
99
|
return buildHtml(result);
|
|
77
100
|
}
|
|
78
101
|
render.__pose = true;
|
|
102
|
+
render.__state = state;
|
|
79
103
|
const el = render;
|
|
80
104
|
Object.defineProperty(el, "classes", {
|
|
81
105
|
get: () => state.classes,
|
|
@@ -84,6 +108,7 @@ function createBuilder(state) {
|
|
|
84
108
|
el.input = (schema) => createBuilder({
|
|
85
109
|
tag: state.tag,
|
|
86
110
|
classes: state.classes,
|
|
111
|
+
attrs: state.attrs,
|
|
87
112
|
children: state.children,
|
|
88
113
|
schema
|
|
89
114
|
});
|
|
@@ -464,22 +489,45 @@ function createBuilder(state) {
|
|
|
464
489
|
el.stroke_w = (n) => dynCls(n, (v) => tw("stroke", v));
|
|
465
490
|
el.sr_only = () => cls("sr-only");
|
|
466
491
|
el.not_sr_only = () => cls("not-sr-only");
|
|
492
|
+
function applyBranch(getBranch) {
|
|
493
|
+
const classEntry = (props) => {
|
|
494
|
+
const branch = getBranch(props);
|
|
495
|
+
return branch ? resolveClasses(branch.classes, props) : "";
|
|
496
|
+
};
|
|
497
|
+
const childEntry = (props) => {
|
|
498
|
+
const branch = getBranch(props);
|
|
499
|
+
if (!branch) return null;
|
|
500
|
+
const branchState = branch.__state;
|
|
501
|
+
if (!branchState.children.length) return null;
|
|
502
|
+
return branchState.children.map((c) => typeof c === "function" ? c(props) : c);
|
|
503
|
+
};
|
|
504
|
+
return derive([classEntry], [childEntry]);
|
|
505
|
+
}
|
|
467
506
|
el.when = (...args) => {
|
|
468
507
|
if (typeof args[0] === "function") {
|
|
469
508
|
const [pred, apply] = args;
|
|
470
|
-
return
|
|
471
|
-
if (!pred(props)) return "";
|
|
472
|
-
return resolveClasses(apply(createBlankBuilder()).classes, props);
|
|
473
|
-
}]);
|
|
509
|
+
return applyBranch((props) => pred(props) ? apply(createBlankBuilder()) : null);
|
|
474
510
|
} else {
|
|
475
511
|
const [key, cases] = args;
|
|
476
|
-
return
|
|
512
|
+
return applyBranch((props) => {
|
|
477
513
|
const branch = cases[props[key]];
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
}]);
|
|
514
|
+
return branch ? branch(createBlankBuilder()) : null;
|
|
515
|
+
});
|
|
481
516
|
}
|
|
482
517
|
};
|
|
518
|
+
el.attr = (name, value) => derive([], [], [[
|
|
519
|
+
"single",
|
|
520
|
+
name,
|
|
521
|
+
value
|
|
522
|
+
]]);
|
|
523
|
+
el.attrs = (recordOrFn) => {
|
|
524
|
+
if (typeof recordOrFn === "function") return derive([], [], [["record", recordOrFn]]);
|
|
525
|
+
return derive([], [], Object.entries(recordOrFn).map(([name, value]) => [
|
|
526
|
+
"single",
|
|
527
|
+
name,
|
|
528
|
+
value
|
|
529
|
+
]));
|
|
530
|
+
};
|
|
483
531
|
el.cls = (value) => typeof value === "function" ? derive([value]) : derive([value]);
|
|
484
532
|
el.render = async (props, opts) => {
|
|
485
533
|
const html = render(props);
|
|
@@ -493,6 +541,7 @@ function createBuilder(state) {
|
|
|
493
541
|
el.child = (value) => createBuilder({
|
|
494
542
|
...state,
|
|
495
543
|
classes: [...state.classes],
|
|
544
|
+
attrs: [...state.attrs],
|
|
496
545
|
children: [...state.children, value]
|
|
497
546
|
});
|
|
498
547
|
return el;
|
|
@@ -509,6 +558,7 @@ const pose = { as(tag) {
|
|
|
509
558
|
return createBuilder({
|
|
510
559
|
tag,
|
|
511
560
|
classes: [],
|
|
561
|
+
attrs: [],
|
|
512
562
|
children: [],
|
|
513
563
|
schema: void 0
|
|
514
564
|
});
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poseui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Type-safe HTML templating engine on steroids",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryuz <ryuzer@proton.me>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/guarana-studio/
|
|
9
|
+
"url": "git+https://github.com/guarana-studio/pose.git"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist"
|