diff --git a/config/fields/checkboxes.php b/config/fields/checkboxes.php
index 00a94b83dc..4be32f5cd7 100644
--- a/config/fields/checkboxes.php
+++ b/config/fields/checkboxes.php
@@ -14,6 +14,13 @@
'icon' => null,
'placeholder' => null,
+ /**
+ * Show/hide the batch select toggle
+ */
+ 'batch' => function (bool $batch = false) {
+ return $batch;
+ },
+
/**
* Arranges the checkboxes in the given number of columns
*/
diff --git a/i18n/translations/en.json b/i18n/translations/en.json
index 90e9e98b90..ff62ccc328 100644
--- a/i18n/translations/en.json
+++ b/i18n/translations/en.json
@@ -44,6 +44,9 @@
"delete": "Delete",
"delete.all": "Delete all",
+ "deselect": "Deselect",
+ "deselect.all": "Deselect all",
+
"dialog.fields.empty": "This dialog has no fields",
"dialog.files.empty": "No files to select",
"dialog.pages.empty": "No pages to select",
@@ -664,6 +667,7 @@
"security": "Security",
"select": "Select",
+ "select.all": "Select all",
"server": "Server",
"settings": "Settings",
"show": "Show",
diff --git a/panel/public/img/icons.svg b/panel/public/img/icons.svg
index 70b799e9ae..a1ae989a1b 100644
--- a/panel/public/img/icons.svg
+++ b/panel/public/img/icons.svg
@@ -157,6 +157,9 @@
+
+
+
@@ -523,6 +526,9 @@
+
+
+
diff --git a/panel/src/components/Forms/Field/CheckboxesField.vue b/panel/src/components/Forms/Field/CheckboxesField.vue
index e8c9c75a45..dcc621ef93 100644
--- a/panel/src/components/Forms/Field/CheckboxesField.vue
+++ b/panel/src/components/Forms/Field/CheckboxesField.vue
@@ -6,6 +6,38 @@
:input="id + '-0'"
:style="$attrs.style"
>
+
+
+
+
+
+ {{ $t("deselect.all") }}
+
+
+ {{ $t("select.all") }}
+
+
+
+
choice.value);
+ this.$emit("input", this.selected);
}
}
};
diff --git a/panel/src/styles/reset/choice.css b/panel/src/styles/reset/choice.css
index 4bc8a97516..85b4a66d70 100644
--- a/panel/src/styles/reset/choice.css
+++ b/panel/src/styles/reset/choice.css
@@ -58,7 +58,8 @@ input:where([type="checkbox"]):checked {
}
/** Checked state **/
-input:where([type="checkbox"], [type="radio"]):checked::after {
+input:where([type="checkbox"], [type="radio"]):checked::after,
+input:where([type="checkbox"]):indeterminate::after {
background: var(--choice-color-checked);
display: grid;
}
@@ -78,14 +79,33 @@ input:where([type="checkbox"], [type="radio"])[disabled] {
}
/** Checkbox & Toggle **/
-input[type="checkbox"]:checked::after {
+input[type="checkbox"]:checked::after,
+input[type="checkbox"]:indeterminate::after {
content: "✓";
inset: 0;
+ place-items: center;
font-weight: 700;
color: var(--choice-color-icon);
line-height: 1;
}
+/** Checkbox indeterminate **/
+input[type="checkbox"]:indeterminate::after {
+ content: "";
+}
+input[type="checkbox"]:indeterminate::before {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ content: "";
+ margin-top: -1px;
+ margin-left: -4px;
+ width: calc(var(--choice-height) - 8px);
+ height: 2px;
+ background: var(--choice-color-icon);
+ z-index: 1;
+}
+
/** Radio **/
input[type="radio"] {
--choice-rounded: 50%;
diff --git a/tests/Form/Field/CheckboxesFieldTest.php b/tests/Form/Field/CheckboxesFieldTest.php
index 256102ab43..4a0ec0d770 100644
--- a/tests/Form/Field/CheckboxesFieldTest.php
+++ b/tests/Form/Field/CheckboxesFieldTest.php
@@ -136,4 +136,18 @@ public function testRequiredValid(): void
$this->assertTrue($field->isValid());
}
+
+ public function testBatch(): void
+ {
+ $field = $this->field('checkboxes');
+
+ $this->assertFalse($field->batch());
+
+ $field = $this->field('checkboxes', [
+ 'batch' => true
+ ]);
+
+ $this->assertTrue($field->batch());
+ }
+
}