Skip to content

Commit 0ac798d

Browse files
[6.x] Forms 2: Field numbering (#14909)
1 parent 9b7ae88 commit 0ac798d

14 files changed

Lines changed: 190 additions & 18 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script setup lang="ts">
2+
import { computed, inject, type ComputedRef } from 'vue';
3+
4+
const props = defineProps<{
5+
fieldKey?: string | null;
6+
}>();
7+
8+
const fieldNumbers = inject<ComputedRef<Map<string, number>> | null>('fieldNumbers', null);
9+
10+
const number = computed(() => (props.fieldKey ? fieldNumbers?.value?.get(props.fieldKey) ?? null : null));
11+
</script>
12+
13+
<template>
14+
<span
15+
v-if="number"
16+
data-field-number
17+
class="font-mono text-2xs text-gray-500 tabular-nums dark:text-gray-400"
18+
>{{ number }}.</span>
19+
</template>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script setup lang="ts">
2+
import { computed } from 'vue';
3+
import { ToggleGroup, ToggleItem } from '@ui';
4+
import { useFieldNumberingPreference } from '@/composables/forms/field-numbering';
5+
6+
const { showFieldNumbers } = useFieldNumberingPreference();
7+
8+
const toggle = () => showFieldNumbers.value = !showFieldNumbers.value;
9+
const label = computed(() => showFieldNumbers.value ? __('Hide field numbers') : __('Show field numbers'));
10+
</script>
11+
12+
<template>
13+
<ToggleGroup :model-value="showFieldNumbers ? 'on' : null" size="xs">
14+
<ToggleItem
15+
value="on"
16+
icon="mail-sign-hashtag"
17+
:aria-label="label"
18+
v-tooltip="label"
19+
@click.prevent="toggle"
20+
/>
21+
</ToggleGroup>
22+
</template>

resources/js/components/forms/builder/FieldInspector.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import axios from 'axios';
55
import { injectBuilderContext } from '@/pages/forms/Builder.vue';
66
import FieldValidationBuilder from '@/components/field-validation/Builder.vue';
77
import FieldConditionsBuilder from '@/components/field-conditions/Builder.vue';
8+
import FieldNumber from '@/components/forms/FieldNumber.vue';
89
import { categories, categoryColorClasses } from './categories';
910
import debounce from '@/util/debounce';
1011
@@ -267,6 +268,7 @@ onMounted(() => load());
267268
>
268269
<template #field-option="{ value, label }">
269270
<span class="inline-flex items-center gap-2">
271+
<FieldNumber :field-key="value" />
270272
<Icon
271273
v-if="findSuggestableField(value)?.icon"
272274
:name="findSuggestableField(value).icon"
@@ -277,6 +279,7 @@ onMounted(() => load());
277279
</template>
278280
<template #field-selected="{ option, field: selectedField }">
279281
<span class="inline-flex items-center gap-2 truncate">
282+
<FieldNumber :field-key="option.value" />
280283
<Icon
281284
v-if="selectedField?.icon"
282285
:name="selectedField.icon"

resources/js/components/forms/builder/ImportField.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { Button, Description, Field, Icon, Label } from '@ui';
33
import { computed } from 'vue';
44
import { usePage } from '@inertiajs/vue3';
5+
import FieldNumber from '@/components/forms/FieldNumber.vue';
56
import { injectBuilderContext, InspectorType } from '@/pages/forms/Builder.vue';
67
import { categoryColorClasses } from './categories';
78
import { __ } from '@/bootstrap/globals';
@@ -77,6 +78,7 @@ const errorMessage = computed(() => {
7778
<template #label>
7879
<Label>
7980
<span class="inline-flex flex-wrap items-center gap-x-2 gap-y-1">
81+
<FieldNumber :field-key="`${field._id}:${fieldsetField.handle}`" class="me-1" />
8082
<Icon name="link" data-collapsed-field-icon class="size-3.5 me-1 " :class="categoryColorClasses['fieldsets']?.icon" aria-hidden="true" />
8183
{{ __(fieldsetField.config.display) }}
8284
<span v-if="fieldsetField.config.validate?.includes('required')" class="relative -top-px ms-0.5 text-red-600">*</span>

resources/js/components/forms/builder/RegularFormField.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Button, Field, Icon, Label } from '@ui';
33
import { computed } from 'vue';
44
import { FieldView, injectBuilderContext, InspectorType } from '@/pages/forms/Builder.vue';
55
import { categories, categoryColorClasses } from './categories';
6+
import FieldNumber from '@/components/forms/FieldNumber.vue';
67
import WidthSelector from '@/components/fields/WidthSelector.vue';
78
import { __ } from '@/bootstrap/globals';
89
@@ -105,6 +106,7 @@ const hasErrors = computed(() => {
105106
>
106107
<template #label>
107108
<Label :class="['mb-0', { 'cursor-pointer': !isInspecting }]">
109+
<FieldNumber :field-key="field._id" class="me-1" />
108110
<Icon :name="field.icon" data-collapsed-field-icon :class="['size-3.5 mb-0.25! me-2.5', iconColorClass]" aria-hidden="true" />
109111
<Icon
110112
v-if="field.config.if || field.config.unless"
@@ -140,6 +142,7 @@ const hasErrors = computed(() => {
140142
>
141143
<template #label>
142144
<Label :class="['', { 'cursor-pointer': !isInspecting }]">
145+
<FieldNumber :field-key="field._id" class="me-1" />
143146
<Icon :name="field.icon" data-collapsed-field-icon :class="['size-3.5 mb-0.25! me-2.5', iconColorClass]" aria-hidden="true" />
144147
<Icon
145148
v-if="field.config.if || field.config.unless"

resources/js/components/forms/builder/pages/PageRule.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Button, Combobox, Icon } from '@ui';
33
import { computed } from 'vue';
44
import { nanoid as uniqid } from 'nanoid';
55
import Condition from '@/components/field-conditions/Condition.vue';
6+
import FieldNumber from '@/components/forms/FieldNumber.vue';
67
import { categories, categoryColorClasses } from '../categories';
78
89
const emit = defineEmits(['update:rule', 'remove']);
@@ -155,6 +156,7 @@ const shouldBeIndented = (index) => {
155156
>
156157
<template #field-option="{ value, label }">
157158
<span class="inline-flex items-center gap-2">
159+
<FieldNumber :field-key="value" />
158160
<Icon
159161
v-if="findSuggestableField(value)?.icon"
160162
:name="findSuggestableField(value).icon"
@@ -165,6 +167,7 @@ const shouldBeIndented = (index) => {
165167
</template>
166168
<template #field-selected="{ option, field: selectedField }">
167169
<span class="inline-flex items-center gap-2 truncate">
170+
<FieldNumber :field-key="option.value" />
168171
<Icon
169172
v-if="selectedField?.icon"
170173
:name="selectedField.icon"

resources/js/components/forms/logic/FieldLogicRule.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
Icon,
1010
Subheading,
1111
} from '@/components/ui';
12+
import FieldNumber from '@/components/forms/FieldNumber.vue';
1213
import FieldConditionsBuilder from '@/components/field-conditions/Builder.vue';
1314
import Converter from '@/components/field-conditions/Converter.js';
1415
import { categories, categoryColorClasses } from '@/components/forms/builder/categories.js';
@@ -121,6 +122,7 @@ const onAlwaysSaveUpdated = (alwaysSave) => emit('update:conditions', { ...props
121122
>
122123
<button type="button" class="show-focus-within_target flex flex-1 items-center gap-1.75 p-2 py-1.75 ps-0 min-w-0 focus:outline-none cursor-pointer" @click="toggleCollapsedState">
123124
<Badge size="lg" pill color="white" class="px-3 text-gray-950 gap-1">
125+
<FieldNumber :field-key="config?.handle" class="me-0.5" />
124126
<Icon
125127
v-if="config?.icon"
126128
:name="config.icon"
@@ -193,6 +195,7 @@ const onAlwaysSaveUpdated = (alwaysSave) => emit('update:conditions', { ...props
193195
>
194196
<template #field-option="{ value, label }">
195197
<span class="inline-flex items-center gap-2">
198+
<FieldNumber :field-key="value" />
196199
<Icon
197200
v-if="findSuggestableField(value)?.icon"
198201
:name="findSuggestableField(value).icon"
@@ -203,6 +206,7 @@ const onAlwaysSaveUpdated = (alwaysSave) => emit('update:conditions', { ...props
203206
</template>
204207
<template #field-selected="{ option, field: selectedField }">
205208
<span class="inline-flex items-center gap-2 truncate">
209+
<FieldNumber :field-key="option.value" />
206210
<Icon
207211
v-if="selectedField?.icon"
208212
:name="selectedField.icon"

resources/js/components/forms/logic/LogicRulePicker.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Primitive } from 'reka-ui';
33
import fuzzysort from 'fuzzysort';
44
import { computed, ref, watch, onBeforeUnmount } from 'vue';
55
import { keys } from '@api';
6+
import FieldNumber from '@/components/forms/FieldNumber.vue';
67
78
const emit = defineEmits(['added']);
89
@@ -140,7 +141,7 @@ onBeforeUnmount(() => unbindKeys());
140141
<ui-icon :name="item.icon || 'plus'" class="size-4" :class="item.iconClass || 'text-gray-600 dark:text-gray-300'" />
141142
<div class="flex-1">
142143
<div class="line-clamp-1 text-sm text-gray-900 dark:text-gray-200">
143-
{{ __(item.display || item.handle) }}
144+
<FieldNumber :field-key="item.handle" class="me-1" />{{ __(item.display || item.handle) }}
144145
</div>
145146
<ui-description v-if="item.instructions" class="w-56 truncate text-2xs">
146147
{{ __(item.instructions) }}

resources/js/components/forms/logic/PageLogicRule.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
Subheading,
1111
} from '@/components/ui';
1212
import PageRule from '@/components/forms/builder/pages/PageRule.vue';
13+
import FieldNumber from '@/components/forms/FieldNumber.vue';
1314
import { categories, categoryColorClasses } from '@/components/forms/builder/categories';
1415
1516
const emit = defineEmits(['collapsed', 'expanded', 'removed', 'update:rule']);
@@ -59,6 +60,7 @@ const firstFieldConfig = computed(() => {
5960
if (!firstCondition?.field) return null;
6061
const field = getFieldConfig(firstCondition.field);
6162
return {
63+
handle: firstCondition.field,
6264
display: field?.config?.display || firstCondition.field,
6365
icon: field?.icon || 'generic-field',
6466
iconClass: getIconClass(field?.category),
@@ -136,6 +138,7 @@ const toggleCollapsedState = () => props.collapsed ? emit('expanded') : emit('co
136138
{{ __('If') }}
137139
</Badge>
138140
<Badge v-if="collapsed && firstFieldConfig" size="lg" pill color="white" class="px-3 text-gray-950 gap-1">
141+
<FieldNumber :field-key="firstFieldConfig.handle" class="me-0.5" />
139142
<Icon
140143
:name="firstFieldConfig.icon"
141144
class="size-3.5 me-1 rounded-sm opacity-100!"
@@ -177,6 +180,7 @@ const toggleCollapsedState = () => props.collapsed ? emit('expanded') : emit('co
177180
</template>
178181
</Subheading>
179182
<Badge v-if="!collapsed && firstFieldConfig" size="lg" pill color="white" class="px-3 text-gray-950 gap-1">
183+
<FieldNumber :field-key="firstFieldConfig.handle" class="me-0.5" />
180184
<Icon
181185
:name="firstFieldConfig.icon"
182186
class="size-3.5 me-0.5 rounded-sm opacity-100!"

resources/js/components/ui/Publish/Form.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ onUnmounted(() => saveKeyBinding.destroy());
9090
<template v-if="!!$slots.title" #title>
9191
<slot name="title" />
9292
</template>
93-
<Button v-if="!readOnly" variant="primary" :text="__('Save')" @click="save" :disabled="saving" />
93+
<slot name="actions">
94+
<Button v-if="!readOnly" variant="primary" :text="__('Save')" @click="save" :disabled="saving" />
95+
</slot>
9496
</Header>
9597
<Container
9698
ref="container"

0 commit comments

Comments
 (0)