@vectoriox/iox-ui 5.29.0 → 5.30.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.
- package/fesm2022/vectoriox-iox-ui-forms-core.mjs +83 -6
- package/fesm2022/vectoriox-iox-ui-forms-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui.mjs +85 -5
- package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
- package/forms-core/index.d.ts +46 -3
- package/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/forms-core/form-presets.ts +10 -6
- package/src/forms-core/nesting.ts +92 -0
- package/src/forms-core/public-api.ts +1 -0
|
@@ -3742,9 +3742,13 @@ var FormPresetId;
|
|
|
3742
3742
|
FormPresetId["Application"] = "application";
|
|
3743
3743
|
FormPresetId["Booking"] = "booking";
|
|
3744
3744
|
})(FormPresetId || (FormPresetId = {}));
|
|
3745
|
-
|
|
3745
|
+
// forms-core is imported by both renderers (the field runtime), and a renderer that never reads the presets
|
|
3746
|
+
|
|
3747
|
+
// to it by form-presets.spec) — a read of `FormPresetId.X` counts as a side effect to a bundler and kept the
|
|
3748
|
+
// whole list in the engine's bundle.
|
|
3749
|
+
const FORM_PRESETS = /* @__PURE__ */ Object.freeze([
|
|
3746
3750
|
{
|
|
3747
|
-
id:
|
|
3751
|
+
id: 'contact', name: 'Contact', icon: 'ph-thin ph-envelope-simple',
|
|
3748
3752
|
description: 'Name, email and a message. Stored, and emailed to you.',
|
|
3749
3753
|
formName: 'Contact form',
|
|
3750
3754
|
fields: [
|
|
@@ -3757,7 +3761,7 @@ const FORM_PRESETS = Object.freeze([
|
|
|
3757
3761
|
errorText: 'We couldn’t send your message. Please try again — your answers are still here.',
|
|
3758
3762
|
},
|
|
3759
3763
|
{
|
|
3760
|
-
id:
|
|
3764
|
+
id: 'newsletter', name: 'Newsletter', icon: 'ph-thin ph-paper-plane-tilt',
|
|
3761
3765
|
description: 'One email field plus consent. Stored.',
|
|
3762
3766
|
formName: 'Newsletter signup',
|
|
3763
3767
|
fields: [
|
|
@@ -3771,7 +3775,7 @@ const FORM_PRESETS = Object.freeze([
|
|
|
3771
3775
|
errorText: 'We couldn’t sign you up. Please try again.',
|
|
3772
3776
|
},
|
|
3773
3777
|
{
|
|
3774
|
-
id:
|
|
3778
|
+
id: 'application', name: 'Application', icon: 'ph-thin ph-read-cv-logo',
|
|
3775
3779
|
description: 'Details plus a CV upload. Stored, with an auto-reply.',
|
|
3776
3780
|
formName: 'Application',
|
|
3777
3781
|
fields: [
|
|
@@ -3787,7 +3791,7 @@ const FORM_PRESETS = Object.freeze([
|
|
|
3787
3791
|
errorText: 'We couldn’t submit your application. Please try again.',
|
|
3788
3792
|
},
|
|
3789
3793
|
{
|
|
3790
|
-
id:
|
|
3794
|
+
id: 'booking', name: 'Booking request', icon: 'ph-thin ph-calendar-check',
|
|
3791
3795
|
description: 'Date and party size. Stored, and it notifies you.',
|
|
3792
3796
|
formName: 'Booking request',
|
|
3793
3797
|
fields: [
|
|
@@ -3807,6 +3811,79 @@ function formPresetById(id) {
|
|
|
3807
3811
|
return FORM_PRESETS.find(p => p.id === id);
|
|
3808
3812
|
}
|
|
3809
3813
|
|
|
3814
|
+
// Where form content may NOT render (Doron, 2026-10-20): inside a link or a button.
|
|
3815
|
+
//
|
|
3816
|
+
// HTML forbids interactive content inside `<a>` and `<button>`, and a click in a field may follow the
|
|
3817
|
+
// link. ONE table and ONE walk, shared by the builder's insert rule (refuse it), the builder's audit
|
|
3818
|
+
// (find it in a page saved before the rule) and the engine (warn about it, never rewrite it).
|
|
3819
|
+
var InteractiveTag;
|
|
3820
|
+
(function (InteractiveTag) {
|
|
3821
|
+
InteractiveTag["Anchor"] = "a";
|
|
3822
|
+
InteractiveTag["Button"] = "button";
|
|
3823
|
+
})(InteractiveTag || (InteractiveTag = {}));
|
|
3824
|
+
/**
|
|
3825
|
+
* Every node type that renders as an interactive element, and the element(s) it can render. A ButtonBlock
|
|
3826
|
+
* renders `<a>` or `<button>` by its `tag` trait — both interactive, so switching the tag never lifts the
|
|
3827
|
+
* rule. Pinned to the engine components' selectors by `interactive-hosts.spec.ts`.
|
|
3828
|
+
*/
|
|
3829
|
+
const INTERACTIVE_HOST_TAGS = Object.freeze({
|
|
3830
|
+
LinkedContainer: [InteractiveTag.Anchor],
|
|
3831
|
+
Link: [InteractiveTag.Anchor],
|
|
3832
|
+
Button: [InteractiveTag.Anchor],
|
|
3833
|
+
ButtonBlock: [InteractiveTag.Anchor, InteractiveTag.Button],
|
|
3834
|
+
SubmitButton: [InteractiveTag.Button],
|
|
3835
|
+
});
|
|
3836
|
+
const FIELD_GROUP_NODE_TYPE = 'FieldGroup';
|
|
3837
|
+
const SUBMIT_BUTTON_NODE_TYPE = 'SubmitButton';
|
|
3838
|
+
/**
|
|
3839
|
+
* Form content — what a link or a button must never hold: a Form (its controls), a Field (an input, select
|
|
3840
|
+
* or textarea), a SubmitButton (a `<button>`) and a FieldGroup (it exists only to hold fields, so it moves as
|
|
3841
|
+
* one). A Success / Error block is text and lives only inside its form, so it is not on the list.
|
|
3842
|
+
*/
|
|
3843
|
+
const FORM_CONTENT_TYPES = new Set([
|
|
3844
|
+
FORM_NODE_TYPE, FIELD_NODE_TYPE, FIELD_GROUP_NODE_TYPE, SUBMIT_BUTTON_NODE_TYPE,
|
|
3845
|
+
]);
|
|
3846
|
+
function isInteractiveHost(node) {
|
|
3847
|
+
return !!node?.type && Object.prototype.hasOwnProperty.call(INTERACTIVE_HOST_TAGS, node.type);
|
|
3848
|
+
}
|
|
3849
|
+
function isFormContent(node) {
|
|
3850
|
+
return !!node?.type && FORM_CONTENT_TYPES.has(node.type);
|
|
3851
|
+
}
|
|
3852
|
+
/**
|
|
3853
|
+
* The first form-content node in `node`'s subtree that sits inside an interactive host, or null. `insideHost`
|
|
3854
|
+
* is true when `node` is placed under a host already — so a Form dropped into a link and a link that holds a
|
|
3855
|
+
* Form are the same finding.
|
|
3856
|
+
*/
|
|
3857
|
+
function formContentInHost(node, insideHost = false) {
|
|
3858
|
+
if (!node)
|
|
3859
|
+
return null;
|
|
3860
|
+
if (insideHost && isFormContent(node))
|
|
3861
|
+
return node;
|
|
3862
|
+
const nowInside = insideHost || isInteractiveHost(node);
|
|
3863
|
+
for (const child of node.children ?? []) {
|
|
3864
|
+
const found = formContentInHost(child, nowInside);
|
|
3865
|
+
if (found)
|
|
3866
|
+
return found;
|
|
3867
|
+
}
|
|
3868
|
+
return null;
|
|
3869
|
+
}
|
|
3870
|
+
/** Every form-content subtree a tree holds inside an interactive host, in document order. */
|
|
3871
|
+
function findFormContentInHosts(roots) {
|
|
3872
|
+
const out = [];
|
|
3873
|
+
const visit = (node, host) => {
|
|
3874
|
+
if (host && isFormContent(node)) {
|
|
3875
|
+
out.push({ host, content: node });
|
|
3876
|
+
return;
|
|
3877
|
+
}
|
|
3878
|
+
const nextHost = host ?? (isInteractiveHost(node) ? node : null);
|
|
3879
|
+
for (const child of (node.children ?? []))
|
|
3880
|
+
visit(child, nextHost);
|
|
3881
|
+
};
|
|
3882
|
+
for (const root of roots ?? [])
|
|
3883
|
+
visit(root, null);
|
|
3884
|
+
return out;
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3810
3887
|
/*
|
|
3811
3888
|
* @vectoriox/iox-ui/forms-core — the shared, FRAMEWORK-FREE core of the forms feature.
|
|
3812
3889
|
*
|
|
@@ -4972,6 +5049,9 @@ class IoxComponentRegistryService {
|
|
|
4972
5049
|
hasComponent(type) {
|
|
4973
5050
|
return this.map.has(type);
|
|
4974
5051
|
}
|
|
5052
|
+
registeredTypes() {
|
|
5053
|
+
return [...this.map.keys()];
|
|
5054
|
+
}
|
|
4975
5055
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: IoxComponentRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4976
5056
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: IoxComponentRegistryService, providedIn: 'root' }); }
|
|
4977
5057
|
}
|