@@ -187,6 +189,7 @@ export default defineComponent({
function stepUp() {
if (!input.value) return;
+ if (props.disabled === true) return;
if (props.value < props.max) {
input.value.stepUp();
@@ -196,6 +199,7 @@ export default defineComponent({
function stepDown() {
if (!input.value) return;
+ if (props.disabled === true) return;
if (props.value > props.min) {
input.value.stepDown();
diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts
index 5c915ae593..6d2fc8caf5 100644
--- a/src/interfaces/index.ts
+++ b/src/interfaces/index.ts
@@ -18,6 +18,7 @@ import InterfaceOneToMany from './one-to-many';
import InterfaceHash from './hash';
import InterfaceSlug from './slug';
import InterfaceUser from './user';
+import InterfaceRepeater from './repeater';
export const interfaces = [
InterfaceTextInput,
@@ -40,6 +41,7 @@ export const interfaces = [
InterfaceHash,
InterfaceSlug,
InterfaceUser,
+ InterfaceRepeater,
];
export default interfaces;
diff --git a/src/interfaces/repeater/index.ts b/src/interfaces/repeater/index.ts
new file mode 100644
index 0000000000..d0ac5c5ed5
--- /dev/null
+++ b/src/interfaces/repeater/index.ts
@@ -0,0 +1,11 @@
+import { defineInterface } from '../define';
+import InterfaceRepeater from './repeater.vue';
+
+export default defineInterface(({ i18n }) => ({
+ id: 'repeater',
+ name: i18n.t('repeater'),
+ icon: 'replay',
+ types: ['json'],
+ component: InterfaceRepeater,
+ options: [],
+}));
diff --git a/src/interfaces/repeater/repeater-row-form.vue b/src/interfaces/repeater/repeater-row-form.vue
new file mode 100644
index 0000000000..1d92350233
--- /dev/null
+++ b/src/interfaces/repeater/repeater-row-form.vue
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/interfaces/repeater/repeater-row-header.vue b/src/interfaces/repeater/repeater-row-header.vue
new file mode 100644
index 0000000000..25c8852cdf
--- /dev/null
+++ b/src/interfaces/repeater/repeater-row-header.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
diff --git a/src/interfaces/repeater/repeater-row.vue b/src/interfaces/repeater/repeater-row.vue
new file mode 100644
index 0000000000..a674734d30
--- /dev/null
+++ b/src/interfaces/repeater/repeater-row.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/interfaces/repeater/repeater.vue b/src/interfaces/repeater/repeater.vue
new file mode 100644
index 0000000000..827ceedcfa
--- /dev/null
+++ b/src/interfaces/repeater/repeater.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lang/en-US/index.json b/src/lang/en-US/index.json
index ea244ae73c..252adcfb9f 100644
--- a/src/lang/en-US/index.json
+++ b/src/lang/en-US/index.json
@@ -177,6 +177,15 @@
"webhooks": "Webhooks",
"roles": "User Roles",
+ "field_width": "Field Width",
+ "field_width_half": "Half Width (Wraps)",
+ "field_width_left": "Half Width (Left Only)",
+ "field_width_right": "Half Width (Right Only)",
+ "field_width_full": "Full Width",
+ "field_width_fill": "Fill the Page",
+
+ "add_a_new_item": "Add a new item...",
+
"add_filter": "Add Filter",
"user_directory": "User Directory",
@@ -306,6 +315,8 @@
"color": "Color",
"circle": "Circle",
+ "empty_item": "Empty Item",
+
"filter": "Filter",
"advanced_filter": "Advanced Filter",
"operators": {
@@ -684,12 +695,6 @@
"field_setup_options": "All set! Just review these interface options...",
"field_type": "Field Type",
"field_updated": "Field Updated",
- "field_width": "Field Width",
- "field_width_half": "Half Width (Wraps)",
- "field_width_left": "Half Width (Left Only)",
- "field_width_right": "Half Width (Right Only)",
- "field_width_full": "Full Width",
- "field_width_fill": "Fill the Page",
"field_width_note": "The width of this field within the form layout. Half-widths wrap based on other fields and their sort order.",
"fields_are_saved_instantly": "Saves Automatically",
"fieldtypes": {
diff --git a/src/styles/_type-styles.scss b/src/styles/_type-styles.scss
index deaa7d9962..da0fc902c5 100644
--- a/src/styles/_type-styles.scss
+++ b/src/styles/_type-styles.scss
@@ -1,3 +1,5 @@
+@import './mixins/type-styles.scss';
+
:root {
--family-monospace: 'Fira Code', monospace;
--family-serif: 'Merriweather', serif;
@@ -5,31 +7,13 @@
}
.type-title {
- color: var(--foreground-normal);
- font-weight: normal;
- font-size: 24px;
- font-family: var(--family-sans-serif);
- font-style: normal;
- line-height: 29px;
- letter-spacing: -0.8px;
+ @include type-title;
}
.type-label {
- color: var(--foreground-normal);
- font-weight: 600;
- font-size: 16px;
- font-family: var(--family-sans-serif);
- font-style: normal;
- line-height: 19px;
- letter-spacing: -0.32px;
+ @include type-label;
}
.type-text {
- color: var(--foreground-normal);
- font-weight: 500;
- font-size: 14px;
- font-family: var(--family-sans-serif);
- font-style: normal;
- line-height: 22px;
- letter-spacing: -0.15px;
+ @include type-text;
}
diff --git a/src/styles/mixins/type-styles.scss b/src/styles/mixins/type-styles.scss
new file mode 100644
index 0000000000..c103d4ee76
--- /dev/null
+++ b/src/styles/mixins/type-styles.scss
@@ -0,0 +1,29 @@
+@mixin type-title {
+ color: var(--foreground-normal);
+ font-weight: normal;
+ font-size: 24px;
+ font-family: var(--family-sans-serif);
+ font-style: normal;
+ line-height: 29px;
+ letter-spacing: -0.8px;
+}
+
+@mixin type-label {
+ color: var(--foreground-normal);
+ font-weight: 600;
+ font-size: 16px;
+ font-family: var(--family-sans-serif);
+ font-style: normal;
+ line-height: 19px;
+ letter-spacing: -0.32px;
+}
+
+@mixin type-text {
+ color: var(--foreground-normal);
+ font-weight: 500;
+ font-size: 14px;
+ font-family: var(--family-sans-serif);
+ font-style: normal;
+ line-height: 22px;
+ letter-spacing: -0.15px;
+}