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
13 changes: 13 additions & 0 deletions resources/js/components/forms/FieldNumber.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup>
defineProps({
number: { type: Number, default: null },
});
</script>

<template>
<span
v-if="number"
data-field-number
class="font-mono text-2xs text-gray-500 tabular-nums dark:text-gray-400"
>{{ number }}.</span>
</template>
22 changes: 22 additions & 0 deletions resources/js/components/forms/FieldNumberingToggle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup>
import { ToggleGroup, ToggleItem } from '@ui';
import { useFieldNumberingPreference } from '@/composables/forms/field-numbering';

const { showFieldNumbers } = useFieldNumberingPreference();

const toggleFieldNumbers = () => {
showFieldNumbers.value = ! showFieldNumbers.value;
};
</script>

<template>
<ToggleGroup :model-value="showFieldNumbers ? 'on' : null" size="xs">
<ToggleItem
value="on"
icon="mail-sign-hashtag"
:aria-label="__('Show field numbers')"
v-tooltip="__('Show field numbers')"
@click.prevent="toggleFieldNumbers"
/>
</ToggleGroup>
</template>
19 changes: 18 additions & 1 deletion resources/js/components/forms/builder/ImportField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { Button, Description, Field, Icon, Label } from '@ui';
import { computed } from 'vue';
import { usePage } from '@inertiajs/vue3';
import FieldNumber from '@/components/forms/FieldNumber.vue';
import { fieldNumberFromMap } from '@/composables/forms/field-numbering';
import { injectBuilderContext, InspectorType } from '@/pages/forms/Builder.vue';
import { categoryColorClasses } from './categories';
import { __ } from '@/bootstrap/globals';
Expand All @@ -16,7 +18,21 @@ defineEmits<{
(e: 'remove'): void;
}>();

const { errors, inspect, inspecting, inspectorType } = injectBuilderContext();
const { errors, fieldNumbers, inspect, inspecting, inspectorType, showFieldNumbers } = injectBuilderContext();

const fieldsetFieldNumber = (fieldsetField) => {
if (! showFieldNumbers?.value) {
return null;
}

const prefix = props.field.prefix || '';

return fieldNumberFromMap(
fieldNumbers?.value,
`${prefix}${fieldsetField.handle}`,
`${props.field._id}:${fieldsetField.handle}`,
);
};

const fieldsets = Object.values(usePage().props.fieldsets);

Expand Down Expand Up @@ -77,6 +93,7 @@ const errorMessage = computed(() => {
<template #label>
<Label>
<span class="inline-flex flex-wrap items-center gap-x-2 gap-y-1">
<FieldNumber :number="fieldsetFieldNumber(fieldsetField)" class="me-1" />
<Icon name="link" data-collapsed-field-icon class="size-3.5 me-1 " :class="categoryColorClasses['fieldsets']?.icon" aria-hidden="true" />
{{ __(fieldsetField.config.display) }}
<span v-if="fieldsetField.config.validate?.includes('required')" class="relative -top-px ms-0.5 text-red-600">*</span>
Expand Down
14 changes: 13 additions & 1 deletion resources/js/components/forms/builder/RegularFormField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { Button, Field, Icon, Label } from '@ui';
import { computed } from 'vue';
import { FieldView, injectBuilderContext, InspectorType } from '@/pages/forms/Builder.vue';
import { categories, categoryColorClasses } from './categories';
import FieldNumber from '@/components/forms/FieldNumber.vue';
import WidthSelector from '@/components/fields/WidthSelector.vue';
import { fieldNumberFromMap } from '@/composables/forms/field-numbering';
import { __ } from '@/bootstrap/globals';

defineEmits<{
Expand All @@ -19,7 +21,15 @@ const props = defineProps<{
isLastRow?: boolean;
}>();

const { dirty, errors, fieldView, inspect, inspecting, inspectorType } = injectBuilderContext();
const { dirty, errors, fieldNumbers, fieldView, inspect, inspecting, inspectorType, showFieldNumbers } = injectBuilderContext();

const fieldNumber = computed(() => {
if (! showFieldNumbers?.value) {
return null;
}

return fieldNumberFromMap(fieldNumbers?.value, props.field.handle, props.field._id);
});

const inspectField = () => inspect(InspectorType.Field, props.field);
const isInspecting = computed(() => inspectorType.value === InspectorType.Field && inspecting.value?._id === props.field._id);
Expand Down Expand Up @@ -105,6 +115,7 @@ const hasErrors = computed(() => {
>
<template #label>
<Label :class="['mb-0', { 'cursor-pointer': !isInspecting }]">
<FieldNumber :number="fieldNumber" class="me-1" />
<Icon :name="field.icon" data-collapsed-field-icon :class="['size-3.5 mb-0.25! me-2.5', iconColorClass]" aria-hidden="true" />
<Icon
v-if="field.config.if || field.config.unless"
Expand Down Expand Up @@ -140,6 +151,7 @@ const hasErrors = computed(() => {
>
<template #label>
<Label :class="['', { 'cursor-pointer': !isInspecting }]">
<FieldNumber :number="fieldNumber" class="me-1" />
<Icon :name="field.icon" data-collapsed-field-icon :class="['size-3.5 mb-0.25! me-2.5', iconColorClass]" aria-hidden="true" />
<Icon
v-if="field.config.if || field.config.unless"
Expand Down
13 changes: 13 additions & 0 deletions resources/js/components/forms/logic/FieldLogic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Button, Card, Heading, Icon, Panel, PanelHeader } from '@ui';
import AddLogicRuleButton from './AddLogicRuleButton.vue';
import FieldLogicRule from './FieldLogicRule.vue';
import { computed, nextTick, ref, watch } from 'vue';
import { fieldNumberFromMap } from '@/composables/forms/field-numbering';
import { categories, categoryColorClasses } from '@/components/forms/builder/categories';
import { KEYS } from '@/components/field-conditions/Constants.js';

Expand All @@ -12,6 +13,8 @@ const props = defineProps({
fields: { type: Array, required: true },
suggestableFields: { type: Array, required: true },
fieldtypes: Array,
showFieldNumbers: { type: Boolean, default: false },
fieldNumbers: { type: Map, default: () => new Map() },
});

const collapsed = ref([]);
Expand Down Expand Up @@ -46,12 +49,21 @@ const getIconClass = (category) => {
return categoryColorClasses[color]?.icon || 'text-gray-600 dark:text-gray-400';
};

const fieldNumber = (field) => {
if (! props.showFieldNumbers) {
return null;
}

return fieldNumberFromMap(props.fieldNumbers, field.handle, field._id);
};

const availableFields = computed(() => {
return fieldsWithoutLogic.value.map(field => ({
handle: field._id,
display: field.display,
icon: field.icon || 'generic-field',
iconClass: getIconClass(field.category),
number: fieldNumber(field),
}));
});

Expand All @@ -60,6 +72,7 @@ const getFieldConfig = (field) => ({
display: field.display,
icon: field.icon || 'generic-field',
iconClass: getIconClass(field.category),
number: fieldNumber(field),
});

const getConditionsConfig = (field) => ({
Expand Down
2 changes: 2 additions & 0 deletions resources/js/components/forms/logic/FieldLogicRule.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Icon,
Subheading,
} from '@/components/ui';
import FieldNumber from '@/components/forms/FieldNumber.vue';
import FieldConditionsBuilder from '@/components/field-conditions/Builder.vue';
import Converter from '@/components/field-conditions/Converter.js';
import { categories, categoryColorClasses } from '@/components/forms/builder/categories.js';
Expand Down Expand Up @@ -121,6 +122,7 @@ const onAlwaysSaveUpdated = (alwaysSave) => emit('update:conditions', { ...props
>
<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">
<Badge size="lg" pill color="white" class="px-3 text-gray-950 gap-1">
<FieldNumber :number="config?.number" class="me-0.5" />
<Icon
v-if="config?.icon"
:name="config.icon"
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/forms/logic/LogicRulePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ onBeforeUnmount(() => unbindKeys());
<ui-icon :name="item.icon || 'plus'" class="size-4" :class="item.iconClass || 'text-gray-600 dark:text-gray-300'" />
<div class="flex-1">
<div class="line-clamp-1 text-sm text-gray-900 dark:text-gray-200">
{{ __(item.display || item.handle) }}
<span v-if="item.number" class="font-mono text-2xs text-gray-500 tabular-nums me-1 dark:text-gray-400">{{ item.number }}.</span>{{ __(item.display || item.handle) }}
</div>
<ui-description v-if="item.instructions" class="w-56 truncate text-2xs">
{{ __(item.instructions) }}
Expand Down
92 changes: 92 additions & 0 deletions resources/js/composables/forms/field-numbering.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { ref, watch } from 'vue';
import { preferences } from '@api';

export const FIELD_NUMBERING_PREFERENCE_KEY = 'forms.field_numbering';

let state = null;

export function useFieldNumberingPreference() {
if (state) {
return state;
}

const showFieldNumbers = ref(preferences.get(FIELD_NUMBERING_PREFERENCE_KEY, false));

watch(showFieldNumbers, (value) => {
preferences.set(FIELD_NUMBERING_PREFERENCE_KEY, value);
});

state = { showFieldNumbers };

return state;
}

export function buildFieldNumbersFromLogicFields(fields) {
const map = new Map();
let n = 1;

(fields || []).forEach((field) => {
map.set(field.handle, n);

if (field._id) {
map.set(field._id, n);
}

n++;
});

return map;
}

export function buildFieldNumbersFromBuilderPages(pages, fieldsets) {
const map = new Map();
let n = 1;
const fieldsetList = fieldsets
? (Array.isArray(fieldsets) ? fieldsets : Object.values(fieldsets))
: [];

(pages || []).forEach((page) => {
(page.sections || []).forEach((section) => {
(section.fields || []).forEach((fieldConfig) => {
if (fieldConfig.type === 'link_fields') {
return;
}

if (fieldConfig.type === 'import') {
const fieldset = fieldsetList.find((fs) => fs.handle === fieldConfig.fieldset);

(fieldset?.fields || [])
.filter((f) => f.type !== 'import')
.forEach((ff) => {
const handle = (fieldConfig.prefix || '') + ff.handle;
map.set(handle, n);
map.set(`${fieldConfig._id}:${ff.handle}`, n);
n++;
});

return;
}

map.set(fieldConfig._id, n);

if (fieldConfig.handle) {
map.set(fieldConfig.handle, n);
}

n++;
});
});
});

return map;
}

export function fieldNumberFromMap(map, ...keys) {
for (const key of keys) {
if (key != null && map.has(key)) {
return map.get(key);
}
}

return null;
}
14 changes: 12 additions & 2 deletions resources/js/pages/forms/Builder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ import Page from '@/components/forms/builder/Page.vue';
import PageInspector from '@/components/forms/builder/PageInspector.vue';
import SectionInspector from '@/components/forms/builder/SectionInspector.vue';
import { useFieldtypeDraggable } from '@/components/forms/builder/use-drag-and-drop';
import FieldNumberingToggle from '@/components/forms/FieldNumberingToggle.vue';
import { buildFieldNumbersFromBuilderPages, useFieldNumberingPreference } from '@/composables/forms/field-numbering';
import { __, uniqid } from '@/bootstrap/globals';
import { progress, keys } from '@api';
import { usePage } from '@inertiajs/vue3';
import type Binding from '@/components/keys/Binding';

defineOptions({ layout: [Layout, PanelLayout, FormsLayout] });
Expand All @@ -66,6 +69,8 @@ const pages = computed(() => formFields.value.pages);
const allSections = computed(() => pages.value.flatMap(page => page.sections));
const shouldShowViewSelector = computed(() => allSections.value.some(section => section.fields.length > 0));
const fieldCount = computed(() => allSections.value.flatMap(section => section.fields).length);
const { showFieldNumbers } = useFieldNumberingPreference();
const fieldNumbers = computed(() => buildFieldNumbersFromBuilderPages(pages.value, usePage().props.fieldsets));

const inspect = (type: InspectorType, data: object): void => {
inspecting.value = data;
Expand Down Expand Up @@ -309,6 +314,8 @@ provideBuilderContext({
errors,
fieldtypes: props.fieldtypes,
fieldView,
fieldNumbers,
showFieldNumbers,
form: props.form,
formsProInstalled: props.formsProInstalled,
inspect,
Expand Down Expand Up @@ -385,7 +392,9 @@ onUnmounted(() => {
{{ __(form.title) }}
</template>
<template #actions>
<ToggleGroup v-if="shouldShowViewSelector" v-model="fieldView" size="xs">
<div class="flex items-center gap-2.5">
<FieldNumberingToggle />
<ToggleGroup v-if="shouldShowViewSelector" v-model="fieldView" size="xs">
<ToggleItem
:value="FieldView.Expanded"
icon="expand"
Expand All @@ -398,7 +407,8 @@ onUnmounted(() => {
:aria-label="__('Collapsed view')"
v-tooltip="__('Collapsed view')"
/>
</ToggleGroup>
</ToggleGroup>
</div>
</template>
</Header>

Expand Down
9 changes: 9 additions & 0 deletions resources/js/pages/forms/Logic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import Layout from '@/pages/layout/Layout.vue';
import PanelLayout from '@/pages/layout/PanelLayout.vue';
import FormsLayout from './Layout.vue';
import { Button, Header, Icon, StatusIndicator } from '@ui';
import FieldNumberingToggle from '@/components/forms/FieldNumberingToggle.vue';
import FieldLogic from '@/components/forms/logic/FieldLogic.vue';
import PageLogic from '@/components/forms/logic/PageLogic.vue';
import Head from '@/pages/layout/Head.vue';
import { buildFieldNumbersFromLogicFields, useFieldNumberingPreference } from '@/composables/forms/field-numbering';
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { keys } from '@api';
import axios from 'axios';
Expand All @@ -22,6 +24,8 @@ const props = defineProps({

const pages = ref(props.pages);
const fields = ref(props.fields);
const { showFieldNumbers } = useFieldNumberingPreference();
const fieldNumbers = computed(() => buildFieldNumbersFromLogicFields(fields.value));
const saving = ref(false);
const saveBinding = ref(null);
const errors = ref({});
Expand Down Expand Up @@ -113,6 +117,9 @@ onUnmounted(() => {
<StatusIndicator status="published" />
{{ __(form.title) }}
</template>
<template #actions>
<FieldNumberingToggle />
</template>
</Header>

<PageLogic
Expand All @@ -127,6 +134,8 @@ onUnmounted(() => {
v-model:fields="fields"
:suggestable-fields
:fieldtypes
:show-field-numbers
:field-numbers
/>
</div>
</template>
Loading