Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/SmarkForm.esm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/SmarkForm.esm.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/SmarkForm.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/SmarkForm.umd.js.map

Large diffs are not rendered by default.

89 changes: 88 additions & 1 deletion docs/_component_types/type_form.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ nav_order: 1
* [Options (import)](#options-import)
* [(Async) clear (Action)](#async-clear-action)
* [Options (clear)](#options-clear)
* [(Async) reset (Action)](#async-reset-action)
* [Options (reset)](#options-reset)
* [Future: null (Action)](#future-null-action)

<!-- vim-markdown-toc -->
" | markdownify }}
Expand Down Expand Up @@ -113,6 +116,61 @@ endcapture %}
%}


### Clear vs Reset Actions Example

The following example demonstrates the distinction between `clear` and `reset` actions:

{% raw %} <!-- capture clear_reset_example {{{ --> {% endraw %}
{% capture clear_reset_example
%}<fieldset data-smark='{"type":"form","name":"userProfile","value":{"name":"John Doe","email":"john@example.com","age":"30"}}'>
<legend>User Profile (with defaults)</legend>
<p>
<label data-smark>Name:</label>
<input data-smark type='text' name='name' />
</p>
<p>
<label data-smark>Email:</label>
<input data-smark type='email' name='email' />
</p>
<p>
<label data-smark>Age:</label>
<input data-smark type='number' name='age' />
</p>
<p>
<button data-smark='{"action":"clear","context":"userProfile"}'>Clear All</button>
<button data-smark='{"action":"reset","context":"userProfile"}'>Reset to Defaults</button>
<button data-smark='{"action":"export"}' onclick='alert(JSON.stringify(data, null, 2))'>Show Data</button>
</p>
</fieldset>{%
endcapture %}
{% raw %} <!-- }}} --> {% endraw %}

{% raw %} <!-- capture clear_reset_example_notes {{{ --> {% endraw %}
{% capture clear_reset_example_notes %}
👉 This form is initialized with default values for all fields.

🔘 **Clear All** - Removes all values, leaving fields empty (ignoring defaults).

🔄 **Reset to Defaults** - Restores the original default values.

💡 Try this sequence:
1. Modify some field values
2. Click "Clear All" - all fields become empty
3. Click "Reset to Defaults" - default values are restored

ℹ️ The `value` option on the form sets the default values that `reset` will restore.
{% endcapture %}
{% raw %} <!-- }}} --> {% endraw %}

{% include components/sampletabs_tpl.md
formId="clear_reset_form"
htmlSource=clear_reset_example
notes=clear_reset_example_notes
showEditor=true
tests=false
%}



API Reference
-------------
Expand Down Expand Up @@ -160,7 +218,11 @@ The `form` component type supports the following actions:

#### (Async) clear (Action)

(Shorhand for `import({})`)
Clears all fields to their type-level empty state, removing all user-provided values **and ignoring any configured default values**. This action is useful when you want to completely empty a form, regardless of any defaults that were set.

For forms, this means setting all fields to empty values (empty strings for text fields, empty arrays for lists, empty objects for nested forms). Unlike `reset`, `clear` does not restore default values.

**Example use case:** A "New" button that clears everything to start fresh, even if the form had default values.

##### Options (clear)

Expand All @@ -170,3 +232,28 @@ The `form` component type supports the following actions:
* **target:**


#### (Async) reset (Action)

Reverts all fields to their configured default values. If a field was initialized with a `value` option or default value, `reset` will restore that value. If no defaults were configured, fields revert to their type-level empty state.

This action is recursive, applying to all nested forms and lists. For lists, if a default structure was provided (e.g., prepopulated items), `reset` will restore that structure.

**Example use case:** A "Reset to defaults" button that restores the form to its initial state as it was when first rendered.

##### Options (reset)

* **action:** (= "reset")
* {{ site.data.definitions.actions.options.origin }}
* {{ site.data.definitions.actions.options.context }}
* **target:**


#### Future: null (Action)

**Note:** This action is planned for future implementation.

The `null` action would explicitly set an entire form or field to `null`, representing an intentionally "not provided" state. This differs from `clear` (which empties fields) and `reset` (which restores defaults).

For nested forms, this would set the entire form value to `null` rather than clearing individual fields. This is useful for optional form sections where you want to distinguish between "empty but provided" and "not provided at all".


13 changes: 12 additions & 1 deletion docs/_component_types/type_input.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,22 @@ Imports a value into the input field.

#### (Async) clear (Action)

Clears the value of the input field.
Clears the value of the input field to an empty string, removing any user-provided value and ignoring any configured default value.

##### Options (clear)

* **action:** (= "clear")
* {{ site.data.definitions.actions.options.origin }}
* {{ site.data.definitions.actions.options.context }}


#### (Async) reset (Action)

Reverts the input field to its configured default value. If the field was initialized with a `value` option, `reset` will restore that value. If no default was configured, the field reverts to an empty string (same as `clear`).

##### Options (reset)

* **action:** (= "reset")
* {{ site.data.definitions.actions.options.origin }}
* {{ site.data.definitions.actions.options.context }}

17 changes: 16 additions & 1 deletion docs/_component_types/type_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,9 @@ The `list` component type supports the following actions:

#### (Async) clear (Action)

(Shorhand for `import([])`)
Clears the list to an empty array, removing all items regardless of any configured default values. Unlike `reset`, this action ignores any prepopulated default items that may have been set via the `value` option.

**Example use case:** A "Clear All" button that removes all items from the list.

##### Options (clear)

Expand All @@ -398,6 +400,19 @@ The `list` component type supports the following actions:
* {{ site.data.definitions.actions.options.context }}


#### (Async) reset (Action)

Reverts the list to its configured default structure. If the list was initialized with prepopulated items via the `value` option, `reset` will restore those items. If no default items were configured, the list reverts to an empty array (same as `clear`).

**Example use case:** A "Reset to defaults" button that restores the list to its initial state.

##### Options (reset)

* **action:** (= "reset")
* {{ site.data.definitions.actions.options.origin }}
* {{ site.data.definitions.actions.options.context }}


#### (Async) addItem (Action)

##### Options (addItem)
Expand Down
6 changes: 3 additions & 3 deletions docs/_data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
},
"homepage": "https://smarkform.bitifet.net",
"devDependencies": {
"@babel/core": "^7.28.3",
"@babel/plugin-proposal-decorators": "^7.28.0",
"@babel/preset-env": "^7.28.3",
"@babel/core": "^7.28.6",
"@babel/plugin-proposal-decorators": "^7.28.6",
"@babel/preset-env": "^7.28.6",
"@playwright/test": "^1.57.0",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-terser": "^0.4.4",
Expand Down
21 changes: 19 additions & 2 deletions src/lib/field.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class SmarkField extends SmarkComponent {
super(...args);
this._isField = true;
this.defaultValue = undefined; // Should be redefined by derived classes.
this.emptyValue = undefined; // Type-level empty value (for clear action)
if (! Object.is(this, this.root)) {
this.name = this.validName(
this.options.name
Expand All @@ -27,7 +28,7 @@ export class SmarkField extends SmarkComponent {
// for modifications to defaultValue in constructors of
// derived classes.
this.defaultValue = this.options.value;
this.clear();
this.reset();
});
if ( this.targetFieldNode) {
this.targetNode.setAttribute("value", this.defaultValue);
Expand All @@ -36,7 +37,23 @@ export class SmarkField extends SmarkComponent {
};
@action
async clear(_data, options = {}) {//{{{
await this.import(undefined, {silent: true, ...options});
// Clear removes all user-provided values, resetting to type-level empty state
// (ignoring any configured defaults)
const clearValue = this.emptyValue !== undefined ? this.emptyValue : undefined;
await this.import(clearValue, {silent: true, ...options});
};//}}}
@action
async reset(_data, options = {}) {//{{{
// Reset reverts to the configured default values (including any prepopulated defaults)
await this.import(this.defaultValue, {silent: true, ...options});
};//}}}
// Note: Future 'null' action would explicitly set the entire form/field to null.
// For nested forms, this would set the form value to null rather than clearing fields.
// Implementation placeholder for when null action is needed:
// @action
// async null(_data, options = {}) {
// // Set the entire form/field to null (not clearing individual fields)
// await this.import(null, {silent: true, ...options});
// }
};

1 change: 1 addition & 0 deletions src/types/color.type.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class color extends input {
super(...args);
const me = this;
me.defaultValue = null; // Default value is null (undefined color)
me.emptyValue = null; // Type-level empty state for clear action
// Add keydown hook to handle "Delete" key:
this.eventHooks.keydown.push ( ev => {
if (ev.defaultPrevented) return;
Expand Down
1 change: 1 addition & 0 deletions src/types/form.type.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class form extends SmarkField {
super(...args);
const me = this;
me.defaultValue = {};
me.emptyValue = {}; // Type-level empty state for clear action
// Focus forms on click (likewise to field types):
this.eventHooks.click.push ( ev => {
if (
Expand Down
1 change: 1 addition & 0 deletions src/types/input.type.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class input extends form {
super(...args);
const me = this;
me.defaultValue = "";
me.emptyValue = ""; // Type-level empty state for clear action
me.eventHooks.keydown.push(
function keydown_hook(ev) {
if (ev.defaultPrevented) return;
Expand Down
1 change: 1 addition & 0 deletions src/types/list.type.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class list extends SmarkField {
super(...args);
const me = this;
me.defaultValue = [];
me.emptyValue = []; // Type-level empty state for clear action
};
async #appendChild(child) {//{{{
const me = this;
Expand Down
Loading