Skip to content
Merged
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
4 changes: 2 additions & 2 deletions allterrain-forms.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Plugin Name: AllTerrain Forms
* Plugin URI: https://github.com/AllTerrainDeveloper/forms
* Description: Forms for WordPress with every premium feature free — conditional logic, calculations, multi-page, file uploads, signatures, repeaters, entry management, ten themes — built as an OpenStation desktop app with a drag-and-drop builder.
* Version: 1.0.0
* Version: 1.0.1
* Requires at least: 6.9
* Requires PHP: 7.4
* Requires Plugins: desktop-mode
Expand Down Expand Up @@ -55,7 +55,7 @@

defined( 'ABSPATH' ) || exit;

define( 'ALLTFO_VERSION', '1.0.0' );
define( 'ALLTFO_VERSION', '1.0.1' );
define( 'ALLTFO_FILE', __FILE__ );
define( 'ALLTFO_DIR', plugin_dir_path( __FILE__ ) );
define( 'ALLTFO_URL', plugin_dir_url( __FILE__ ) );
Expand Down
109 changes: 88 additions & 21 deletions assets/css/builder.css
Original file line number Diff line number Diff line change
Expand Up @@ -4053,6 +4053,59 @@ os-checkbox-label.atfb-check {
}
}

/* Condition dialogs use the shell's dialog palette, including themed textures. */
.atfb-condition-editor,
.atfb-condition-copy {
--atfb-bg: var( --os-ui-modal-bg, var( --os-window-bg, #fff ) );
--atfb-fg: var( --os-ui-modal-text, var( --os-ui-fg, #1e1e1e ) );
--atfb-muted: var( --os-ui-modal-text-muted, var( --os-ui-fg-muted, #646970 ) );
--atfb-border: var( --os-ui-modal-border, var( --os-ui-border, #dcdcde ) );
--atfb-border-strong: var( --os-ui-modal-border-strong, var( --os-ui-border-strong, #c3c4c7 ) );
--atfb-well: var( --os-ui-modal-field-bg, var( --os-ui-surface-sunken, #f6f7f7 ) );
--atfb-surface: var( --os-ui-modal-surface, var( --os-ui-surface, #fff ) );
--atfb-raised: var( --os-ui-modal-surface-elevated, var( --os-ui-surface-elevated, #f6f7f7 ) );
--atfb-hover: var( --os-ui-modal-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
color: var( --atfb-fg );
background-color: var( --atfb-bg );
background-image: var( --os-ui-dialog-bg-image, none );
background-repeat: var( --os-ui-dialog-bg-image-repeat, repeat );
background-size: var( --os-ui-dialog-bg-image-size, auto );
background-position: var( --os-ui-dialog-bg-image-position, center );
border: 1px solid var( --atfb-border );
border-radius: var( --os-window-radius, var( --atfb-radius ) );
box-shadow: var( --os-window-shadow-focused, 0 20px 60px rgba( 0, 0, 0, 0.35 ) );
}

/* Component hosts inherit the same modal palette as the native controls. */
:is( .atfb-condition-editor, .atfb-condition-copy ) :is( os-select, os-button, os-checkbox-label ) {
--os-ui-fg: var( --atfb-fg );
--os-ui-fg-muted: var( --atfb-muted );
--os-ui-border: var( --atfb-border );
--os-ui-border-strong: var( --atfb-border-strong );
--os-window-bg: var( --atfb-well );
--os-ui-surface: var( --atfb-surface );
--os-ui-surface-elevated: var( --atfb-raised );
--os-ui-hover: var( --atfb-hover );
--os-ui-button-bg-hover: var( --os-ui-modal-button-bg-hover, var( --atfb-hover ) );
}

:is( .atfb-condition-editor, .atfb-condition-copy ) h2 {
color: var( --atfb-fg );
}

.atfb-logic-actions {
display: flex;
justify-content: flex-end;
margin-block-start: var( --atfb-gap );
}

.atfb-copy-condition {
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}

/* Keep copied conditions readable even when a rule contains a long answer. */
.atfb-condition-copy__preview {
padding: 12px;
Expand Down Expand Up @@ -4241,7 +4294,7 @@ os-checkbox-label.atfb-check {

.atfb-condition-editor {
inline-size: min( 580px, 100% );
padding: 24px;
padding: calc( 2 * var( --atfb-gap ) );
}

.atfb-condition-editor__heading {
Expand All @@ -4267,22 +4320,11 @@ os-checkbox-label.atfb-check {
place-items: center;
inline-size: 40px;
block-size: 40px;
border-radius: 10px;
border-radius: var( --atfb-radius-ctl );
background: var( --atfb-accent-soft );
color: var( --atfb-accent );
}

.atfb-condition-editor__switch {
padding-block-end: 18px;
border-block-end: 1px solid var( --atfb-border );
}

.atfb-condition-editor__switch p {
margin: 6px 0 0;
font-size: 12px;
color: var( --atfb-muted );
}

.atfb-condition-editor__sentence {
display: flex;
align-items: center;
Expand All @@ -4293,13 +4335,6 @@ os-checkbox-label.atfb-check {
color: var( --atfb-muted );
}

.atfb-condition-editor__empty {
margin-block: 20px;
font-size: 13px;
line-height: 1.6;
color: var( --atfb-muted );
}

.atfb-condition-editor .atfb-rule {
display: grid;
grid-template-columns: minmax( 0, 1.2fr ) minmax( 0, 1fr ) minmax( 100px, 0.6fr ) auto;
Expand Down Expand Up @@ -4328,12 +4363,16 @@ os-checkbox-label.atfb-check {
}

.atfb-condition-editor__footer .atfb-modal__actions {
flex-wrap: wrap;
margin: 0;
margin-inline-start: auto;
}

.atfb-condition-editor .atfb-rule input {
background: var( --atfb-well );
color: var( --atfb-fg );
border: 1px solid var( --atfb-border );
border-radius: 6px;
border-radius: var( --atfb-radius-ctl );
min-inline-size: 100px;
inline-size: 100%;
}
Expand All @@ -4343,3 +4382,31 @@ os-checkbox-label.atfb-check {
.atfb-rule input {
min-inline-size: 100px;
}

@media ( max-width: 600px ) {
.atfb-condition-editor .atfb-rule {
grid-template-columns: minmax( 0, 1fr ) auto;
}

.atfb-condition-editor .atfb-rule__top > .atfb-card__action {
grid-column: 2;
}

.atfb-condition-editor .atfb-rule > :not( .atfb-rule__top ) {
grid-column: 1;
}
}

.atfb-condition-editor .atfb-card__action {
color: var( --atfb-muted );
border-radius: var( --atfb-radius-ctl );
}

.atfb-condition-editor .atfb-card__action:hover {
color: var( --atfb-danger );
background: var( --atfb-danger-soft );
}

.atfb-condition-editor input::placeholder {
color: var( --atfb-muted );
}
10 changes: 9 additions & 1 deletion assets/css/form.css
Original file line number Diff line number Diff line change
Expand Up @@ -894,11 +894,19 @@
color: var( --atf-text-muted );
}

.atf-form .atf-total__input {
.atf-form .atf-total__input,
.atf-total__output {
font-variant-numeric: tabular-nums;
font-weight: 600;
}

.atf-total__output {
display: block;
color: var( --atf-text );
font-size: var( --atf-size-base );
overflow-wrap: anywhere;
}

/* -------------------------------------------------------------------------- */
/* Composite and repeater */
/* -------------------------------------------------------------------------- */
Expand Down
53 changes: 34 additions & 19 deletions assets/js/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -1143,7 +1143,7 @@ var allTerrainFormsBuilder = function(exports) {
color: "text",
name: "composite",
address: "composite",
total: "text",
total: "total",
hidden: "static",
heading: "static",
html: "static",
Expand Down Expand Up @@ -1254,6 +1254,11 @@ var allTerrainFormsBuilder = function(exports) {
}
function control(field, type, shape, handlers) {
switch (shape) {
case "total":
return el("div", { class: "atf-total", children: [
field.currency ? el("span", { class: "atf-total__currency", text: String(field.currency) }) : null,
field.display === "output" ? el("output", { class: "atf-total__output", text: "0.00" }) : el("input", { class: "atf-input atf-total__input", type: "text", value: "0.00", attrs: { disabled: true } })
] });
case "text":
return placeholderBox(field, "atf-input", handlers);
case "textarea":
Expand Down Expand Up @@ -5274,6 +5279,13 @@ var allTerrainFormsBuilder = function(exports) {
row("Currency symbol", textInput(String(field.currency ?? ""), (value) => update("currency", value)))
);
}
if (field.type === "total") {
this.inspector.append(row("Display total as", select(
String(field.display ?? "input"),
[{ value: "output", label: "Plain text" }, { value: "input", label: "Disabled input" }],
(value) => update("display", value)
)));
}
this.inspector.append(this.renderValidationSection(field, supports, update));
if (!parent) {
this.inspector.append(this.renderLogicSection(field));
Expand Down Expand Up @@ -6841,7 +6853,8 @@ var allTerrainFormsBuilder = function(exports) {
if (!field) {
return;
}
const draft = { ...field.logic, rules: field.logic.rules.map((rule) => ({ ...rule })) };
const hadRules = field.logic.rules.length > 0;
const draft = { ...field.logic, enabled: true, rules: field.logic.rules.map((rule) => ({ ...rule })) };
const overlay = el("div", { class: "atfb-overlay" });
const dialog = el("div", {
class: "atfb-modal atfb-condition-editor",
Expand Down Expand Up @@ -6870,7 +6883,7 @@ var allTerrainFormsBuilder = function(exports) {
});
match.setAttribute("aria-label", "Match rules");
const copy = el("button", {
class: "atfb-copy-condition",
class: "atfb-button atfb-copy-condition",
type: "button",
children: [icon("admin-page"), el("span", { text: "Copy condition" })],
on: { click: () => openConditionCopy({
Expand All @@ -6881,6 +6894,7 @@ var allTerrainFormsBuilder = function(exports) {
fields: this.schema.fields.map((item) => item.id === fieldId ? { ...item, logic: draft } : item)
} : null,
onCopy: () => {
draft.enabled = true;
paint();
dialog.focus();
}
Expand All @@ -6891,23 +6905,21 @@ var allTerrainFormsBuilder = function(exports) {
el("span", { class: "atfb-condition-editor__icon", children: [icon("randomize")] }),
el("div", { children: [el("h2", { text: "Conditional logic" }), el("p", { text: field.label || "Untitled field" })] })
] }),
el("div", { class: "atfb-condition-editor__switch", children: [
checkbox("Enable conditions", draft.enabled, (enabled) => {
draft.enabled = enabled;
paint();
}),
el("p", { text: "Choose when this field appears in your form." })
] }),
...draft.enabled ? [
el("div", { class: "atfb-condition-editor__sentence", children: [action, "this field when", match, "of these rules match:"] }),
...this.logicRulesEditor(draft, write, fieldId)
] : [el("p", { class: "atfb-condition-editor__empty", text: "This field is always visible. Enable conditions to choose when to show or hide it." })],
el("p", { class: "atfb-hint", text: "Choose when this field appears in your form." }),
el("div", { class: "atfb-condition-editor__sentence", children: [action, "this field when", match, "of these rules match:"] }),
...this.logicRulesEditor(draft, write, fieldId),
el("div", { class: "atfb-condition-editor__footer", children: [
copy,
el("div", { class: "atfb-modal__actions", children: [
button("Cancel", close),
button("Clear", () => {
this.snapshot();
this.editCondition(fieldId, (live) => Object.assign(live, { enabled: false, action: "show", match: "all", rules: [] }));
this.snapshot();
close();
}),
...hadRules || draft.rules.length ? [button("Cancel", close)] : [],
button("Save conditions", () => {
if (!this.liveField(fieldId)) {
if (!this.liveField(fieldId) || !draft.rules.length) {
close();
return;
}
Expand All @@ -6919,6 +6931,9 @@ var allTerrainFormsBuilder = function(exports) {
] })
] })
);
if (!draft.rules.length) {
dialog.querySelector(".atfb-button--primary")?.setAttribute("disabled", "");
}
if (focusedIndex >= 0) {
const controls = dialog.querySelectorAll(controlsSelector);
controls[Math.min(focusedIndex, controls.length - 1)]?.focus();
Expand Down Expand Up @@ -8339,7 +8354,7 @@ var allTerrainFormsBuilder = function(exports) {
/** Reuse a condition, resolving the schema when the chooser applies it. */
copyConditionButton(key) {
return el("button", {
class: "atfb-copy-condition",
class: "atfb-button atfb-copy-condition",
type: "button",
children: [icon("admin-page"), el("span", { text: "Copy condition" })],
on: { click: () => openConditionCopy({
Expand Down Expand Up @@ -8377,7 +8392,6 @@ var allTerrainFormsBuilder = function(exports) {
`logic:${field.id}`,
"Conditional logic",
[
this.copyConditionButton(`field:${field.id}`),
checkbox("Only show this field sometimes", logic.enabled, (value) => {
write((live) => {
live.enabled = value;
Expand Down Expand Up @@ -8414,7 +8428,8 @@ var allTerrainFormsBuilder = function(exports) {
}),
...editor
]
}) : null
}) : null,
el("div", { class: "atfb-logic-actions", children: [this.copyConditionButton(`field:${field.id}`)] })
],
// A field that already has a condition opens showing it: being told a
// rule governs this field and not what it says is the problem the
Expand Down
8 changes: 4 additions & 4 deletions assets/js/builder.min.js

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion assets/js/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -1280,7 +1280,7 @@ var allTerrainFormsFront = function(exports) {
}
return;
}
input.disabled = !show;
input.disabled = !show || input.hasAttribute("data-atf-total");
});
}
const calculated = applyCalculations(this.schema.fields, values);
Expand All @@ -1295,6 +1295,10 @@ var allTerrainFormsFront = function(exports) {
const value = calculated[field.id];
const decimals = typeof field.decimals === "number" ? field.decimals : 2;
input.value = typeof value === "number" ? value.toFixed(decimals) : "";
const submitted = this.fieldElement(field.id)?.querySelector("[data-atf-total-value]");
if (submitted) {
submitted.value = input.value;
}
}
}
/* ---------------------------------------------------------------- Steps */
Expand Down
2 changes: 1 addition & 1 deletion assets/js/form.min.js

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions docs/field-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,14 @@ add_filter( 'alltfo_client_schema', function ( $payload, $schema ) {
return $payload;
}, 10, 2 );
```

### Total display

The Total inspector offers **Display total as**: **Plain text** or **Disabled
input**. The `display` setting accepts `output` or `input` (the default, also used
for older forms and invalid values). Plain text renders a semantic `<output>`
with the field label attached; input mode renders a disabled text input. Both
use the form theme, currency symbol and decimal precision and update live.
A hidden `atf[field_id]` input preserves the calculated value for submission and
integrations. The server recomputes it from the source answers in both modes.
The builder canvas reflects the selected presentation.
11 changes: 7 additions & 4 deletions docs/javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,13 @@ editor continues to calculate its numeric result using the displayed sample
answers.

**Conditional** is a compact button beside Required/Optional in each field
card’s title bar. It opens a dialog with an enable switch, Show/Hide, all/any,
and the full rule editor. Changes stay in a draft until **Save conditions**;
Cancel or Escape discards them. Copy condition inside the dialog also edits
only this draft. An active condition is indicated by the button’s filled dot.
card’s title bar. It opens a dialog with Show/Hide, all/any,
and the full rule editor, ready to add conditions without an enable checkbox.
Changes stay in a draft until **Save conditions**, which enables the condition;
Cancel or Escape discards them. **Clear** removes the saved rules, disables the
condition, and closes the dialog. Cancel is absent when neither the saved
condition nor the draft has rules; Save is disabled for an empty draft. Copy condition inside the dialog also edits
only this draft and enables the copied rules when saved. An active condition is indicated by the button’s filled dot.

The bottom condition strip remains directly editable. The `+ Add rule` button
adds a rule, each rule has a Delete button, and Clear removes all rules while
Expand Down
1 change: 1 addition & 0 deletions includes/field-types.php
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,7 @@ static function ( $item ) {
'formula' => '',
'currency' => '',
'decimals' => 2,
'display' => 'input',
),
'format' => 'alltfo_format_total_value',
'position' => 20,
Expand Down
Loading
Loading