fix: single store state for all undo/redo related changes
This commit is contained in:
parent
99a645bffd
commit
0866a7003e
10 changed files with 57 additions and 97 deletions
|
|
@ -85,7 +85,7 @@ function delete_column(with_children) {
|
|||
|
||||
// remove column
|
||||
columns.splice(index, 1);
|
||||
store.selected_field = null;
|
||||
store.form.selected_field = null;
|
||||
}
|
||||
|
||||
function move_columns_to_section() {
|
||||
|
|
@ -101,7 +101,7 @@ function move_columns_to_section() {
|
|||
store.selected(column.df.name) ? 'selected' : ''
|
||||
]"
|
||||
:title="column.df.fieldname"
|
||||
@click.stop="store.selected_field = column.df"
|
||||
@click.stop="store.form.selected_field = column.df"
|
||||
@mouseover.stop="hovered = true"
|
||||
@mouseout.stop="hovered = false"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ function remove_field() {
|
|||
}
|
||||
let index = props.column.fields.indexOf(props.field);
|
||||
props.column.fields.splice(index, 1);
|
||||
store.selected_field = null;
|
||||
store.form.selected_field = null;
|
||||
}
|
||||
|
||||
function move_fields_to_column() {
|
||||
|
|
@ -51,7 +51,7 @@ function duplicate_field() {
|
|||
// push duplicate_field after props.field in the same column
|
||||
let index = props.column.fields.indexOf(props.field);
|
||||
props.column.fields.splice(index + 1, 0, duplicate_field);
|
||||
store.selected_field = duplicate_field.df;
|
||||
store.form.selected_field = duplicate_field.df;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ function duplicate_field() {
|
|||
store.selected(field.df.name) ? 'selected' : ''
|
||||
]"
|
||||
:title="field.df.fieldname"
|
||||
@click.stop="store.selected_field = field.df"
|
||||
@click.stop="store.form.selected_field = field.df"
|
||||
@mouseover.stop="hovered = true"
|
||||
@mouseout.stop="hovered = false"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -14,18 +14,18 @@ let docfield_df = computed(() => {
|
|||
if (in_list(frappe.model.layout_fields, df.fieldtype) || df.hidden) {
|
||||
return false;
|
||||
}
|
||||
if (df.depends_on && !evaluate_depends_on_value(df.depends_on, store.selected_field)) {
|
||||
if (df.depends_on && !evaluate_depends_on_value(df.depends_on, store.form.selected_field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
in_list(["fetch_from", "fetch_if_empty"], df.fieldname) &&
|
||||
in_list(frappe.model.no_value_type, store.selected_field.fieldtype)
|
||||
in_list(frappe.model.no_value_type, store.form.selected_field.fieldtype)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (df.fieldname === "reqd" && store.selected_field.fieldtype === "Check") {
|
||||
if (df.fieldname === "reqd" && store.form.selected_field.fieldtype === "Check") {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -34,11 +34,11 @@ let docfield_df = computed(() => {
|
|||
df.options = "";
|
||||
args.value = {};
|
||||
|
||||
if (in_list(["Table", "Link"], store.selected_field.fieldtype)) {
|
||||
if (in_list(["Table", "Link"], store.form.selected_field.fieldtype)) {
|
||||
df.fieldtype = "Link";
|
||||
df.options = "DocType";
|
||||
|
||||
if (store.selected_field.fieldtype === "Table") {
|
||||
if (store.form.selected_field.fieldtype === "Table") {
|
||||
args.value.is_table_field = 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -63,14 +63,14 @@ let docfield_df = computed(() => {
|
|||
<template>
|
||||
<SearchBox v-model="search_text" />
|
||||
<div class="control-data">
|
||||
<div v-if="store.selected_field">
|
||||
<div v-if="store.form.selected_field">
|
||||
<div class="field" v-for="(df, i) in docfield_df" :key="i">
|
||||
<component
|
||||
:is="df.fieldtype.replace(' ', '') + 'Control'"
|
||||
:args="args"
|
||||
:df="df"
|
||||
:value="store.selected_field[df.fieldname]"
|
||||
v-model="store.selected_field[df.fieldname]"
|
||||
:value="store.form.selected_field[df.fieldname]"
|
||||
v-model="store.form.selected_field[df.fieldname]"
|
||||
:data-fieldname="df.fieldname"
|
||||
:data-fieldtype="df.fieldtype"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ import { onClickOutside, useMagicKeys, whenever } from "@vueuse/core";
|
|||
let store = useStore();
|
||||
|
||||
let should_render = computed(() => {
|
||||
return Object.keys(store.layout).length !== 0;
|
||||
return Object.keys(store.form.layout).length !== 0;
|
||||
});
|
||||
|
||||
let container = ref(null);
|
||||
onClickOutside(container, () => store.selected_field = null);
|
||||
onClickOutside(container, () => store.form.selected_field = null);
|
||||
|
||||
// cmd/ctrl + s to save the form
|
||||
const { meta_s, ctrl_s } = useMagicKeys();
|
||||
|
|
@ -53,7 +53,7 @@ function setup_change_doctype_dialog() {
|
|||
}
|
||||
|
||||
watch(
|
||||
() => store.layout,
|
||||
() => store.form.layout,
|
||||
() => (store.dirty = true),
|
||||
{ deep: true }
|
||||
);
|
||||
|
|
@ -69,7 +69,7 @@ onMounted(() => {
|
|||
v-if="should_render"
|
||||
ref="container"
|
||||
class="form-builder-container"
|
||||
@click="store.selected_field = null"
|
||||
@click="store.form.selected_field = null"
|
||||
>
|
||||
<div class="form-controls" @click.stop>
|
||||
<div class="form-sidebar">
|
||||
|
|
|
|||
|
|
@ -67,21 +67,21 @@ function delete_section(with_children) {
|
|||
|
||||
// remove section
|
||||
sections.splice(index, 1);
|
||||
store.selected_field = null;
|
||||
store.form.selected_field = null;
|
||||
}
|
||||
|
||||
function select_section() {
|
||||
if (props.section.df.collapsible) {
|
||||
collapsed.value = !collapsed.value;
|
||||
}
|
||||
store.selected_field = props.section.df;
|
||||
store.form.selected_field = props.section.df;
|
||||
}
|
||||
|
||||
function move_sections_to_tab() {
|
||||
let new_tab = move_children_to_parent(props, "tab", "section", store.layout);
|
||||
let new_tab = move_children_to_parent(props, "tab", "section", store.form.layout);
|
||||
|
||||
// activate tab
|
||||
store.active_tab = new_tab;
|
||||
store.form.active_tab = new_tab;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ function resize(e) {
|
|||
}
|
||||
|
||||
watch(
|
||||
() => store.selected_field,
|
||||
() => store.form.selected_field,
|
||||
value => {
|
||||
active_tab.value = value ? tab_titles[1] : tab_titles[0];
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ import { ref, computed, nextTick } from "vue";
|
|||
let store = useStore();
|
||||
|
||||
let dragged = ref(false);
|
||||
let has_tabs = computed(() => store.layout.tabs.length > 1);
|
||||
store.active_tab = store.layout.tabs[0].df.name;
|
||||
let has_tabs = computed(() => store.form.layout.tabs.length > 1);
|
||||
store.form.active_tab = store.form.layout.tabs[0].df.name;
|
||||
|
||||
function activate_tab(tab) {
|
||||
store.active_tab = tab.df.name;
|
||||
store.selected_field = tab.df;
|
||||
store.form.active_tab = tab.df.name;
|
||||
store.form.selected_field = tab.df;
|
||||
|
||||
// scroll to active tab
|
||||
nextTick(() => {
|
||||
|
|
@ -28,24 +28,24 @@ function activate_tab(tab) {
|
|||
function drag_over(tab) {
|
||||
!dragged.value &&
|
||||
setTimeout(() => {
|
||||
store.active_tab = tab.df.name;
|
||||
store.form.active_tab = tab.df.name;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function add_new_tab() {
|
||||
let tab = {
|
||||
df: store.get_df("Tab Break", "", "Tab " + (store.layout.tabs.length + 1)),
|
||||
df: store.get_df("Tab Break", "", "Tab " + (store.form.layout.tabs.length + 1)),
|
||||
sections: [section_boilerplate()],
|
||||
};
|
||||
|
||||
store.layout.tabs.push(tab);
|
||||
store.form.layout.tabs.push(tab);
|
||||
activate_tab(tab);
|
||||
}
|
||||
|
||||
function add_new_section() {
|
||||
let section = section_boilerplate();
|
||||
store.current_tab.sections.push(section);
|
||||
store.selected_field = section.df;
|
||||
store.form.selected_field = section.df;
|
||||
}
|
||||
|
||||
function is_current_tab_empty() {
|
||||
|
|
@ -76,7 +76,7 @@ function remove_tab() {
|
|||
}
|
||||
|
||||
function delete_tab(with_children) {
|
||||
let tabs = store.layout.tabs;
|
||||
let tabs = store.form.layout.tabs;
|
||||
let index = tabs.indexOf(store.current_tab);
|
||||
|
||||
if (!with_children) {
|
||||
|
|
@ -102,17 +102,17 @@ function delete_tab(with_children) {
|
|||
|
||||
// activate previous tab
|
||||
let prev_tab_index = index == 0 ? 0 : index - 1;
|
||||
store.active_tab = tabs[prev_tab_index].df.name;
|
||||
store.selected_field = null;
|
||||
store.form.active_tab = tabs[prev_tab_index].df.name;
|
||||
store.form.selected_field = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tab-header" v-if="!(store.layout.tabs.length == 1 && store.read_only)">
|
||||
<div class="tab-header" v-if="!(store.form.layout.tabs.length == 1 && store.read_only)">
|
||||
<draggable
|
||||
v-show="has_tabs"
|
||||
class="tabs"
|
||||
v-model="store.layout.tabs"
|
||||
v-model="store.form.layout.tabs"
|
||||
group="tabs"
|
||||
filter="[data-has-std-field='true']"
|
||||
:prevent-on-filter="false"
|
||||
|
|
@ -123,7 +123,7 @@ function delete_tab(with_children) {
|
|||
>
|
||||
<template #item="{ element }">
|
||||
<div
|
||||
:class="['tab', store.active_tab == element.df.name ? 'active' : '']"
|
||||
:class="['tab', store.form.active_tab == element.df.name ? 'active' : '']"
|
||||
:title="element.df.fieldname"
|
||||
:data-is-custom="element.df.is_custom_field"
|
||||
:data-has-std-field="store.has_standard_field(element)"
|
||||
|
|
@ -166,9 +166,9 @@ function delete_tab(with_children) {
|
|||
<div class="tab-contents">
|
||||
<div
|
||||
class="tab-content"
|
||||
v-for="(tab, i) in store.layout.tabs"
|
||||
v-for="(tab, i) in store.form.layout.tabs"
|
||||
:key="i"
|
||||
:class="[store.active_tab == tab.df.name ? 'active' : '']"
|
||||
:class="[store.form.active_tab == tab.df.name ? 'active' : '']"
|
||||
>
|
||||
<draggable
|
||||
class="tab-content-container"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ let doctype_df = computed(() => {
|
|||
doctypes.value = store
|
||||
.get_updated_fields()
|
||||
.filter(df => df.fieldtype == "Link")
|
||||
.filter(df => df.options && df.fieldname != store.selected_field.fieldname)
|
||||
.filter(df => df.options && df.fieldname != store.form.selected_field.fieldname)
|
||||
.sort((a, b) => a.options.localeCompare(b.options))
|
||||
.map(df => ({
|
||||
label: `${df.options} (${df.fieldname})`,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { create_layout, scrub_field_names, get_field_by_name } from "./utils";
|
||||
import { computed, nextTick, ref, watch } from "vue";
|
||||
import { create_layout, scrub_field_names } from "./utils";
|
||||
import { computed, nextTick, ref } from "vue";
|
||||
import { useDebouncedRefHistory, onKeyDown } from "@vueuse/core";
|
||||
|
||||
export const useStore = defineStore("form-builder-store", () => {
|
||||
|
|
@ -8,15 +8,17 @@ export const useStore = defineStore("form-builder-store", () => {
|
|||
let doc = ref(null);
|
||||
let docfields = ref([]);
|
||||
let custom_docfields = ref([]);
|
||||
let layout = ref({});
|
||||
let active_tab = ref("");
|
||||
let selected_field = ref(null);
|
||||
let form = ref({
|
||||
layout: {},
|
||||
active_tab: null,
|
||||
selected_field: null,
|
||||
});
|
||||
let dirty = ref(false);
|
||||
let read_only = ref(false);
|
||||
let is_customize_form = ref(false);
|
||||
let preview = ref(false);
|
||||
let drag = ref(false);
|
||||
let get_animation = ref("cubic-bezier(0.34, 1.56, 0.64, 1)");
|
||||
let get_animation = "cubic-bezier(0.34, 1.56, 0.64, 1)";
|
||||
let ref_history = ref(null);
|
||||
|
||||
// Getters
|
||||
|
|
@ -25,12 +27,12 @@ export const useStore = defineStore("form-builder-store", () => {
|
|||
});
|
||||
|
||||
let current_tab = computed(() => {
|
||||
return layout.value.tabs.find((tab) => tab.df.name == active_tab.value);
|
||||
return form.value.layout.tabs.find((tab) => tab.df.name == form.value.active_tab);
|
||||
});
|
||||
|
||||
// Actions
|
||||
function selected(name) {
|
||||
return selected_field.value?.name == name;
|
||||
return form.value.selected_field?.name == name;
|
||||
}
|
||||
|
||||
function get_df(fieldtype, fieldname = "", label = "") {
|
||||
|
|
@ -87,9 +89,9 @@ export const useStore = defineStore("form-builder-store", () => {
|
|||
}
|
||||
}
|
||||
|
||||
layout.value = get_layout();
|
||||
active_tab.value = layout.value.tabs[0].df.name;
|
||||
selected_field.value = null;
|
||||
form.value.layout = get_layout();
|
||||
form.value.active_tab = form.value.layout.tabs[0].df.name;
|
||||
form.value.selected_field = null;
|
||||
|
||||
nextTick(() => {
|
||||
dirty.value = false;
|
||||
|
|
@ -101,8 +103,6 @@ export const useStore = defineStore("form-builder-store", () => {
|
|||
setup_undo_redo();
|
||||
}
|
||||
|
||||
let data = ref({ active_tab, layout, selected_field });
|
||||
|
||||
let undo_redo_keyboard_event = onKeyDown(true, (e) => {
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
if (e.key === "z" && !e.shiftKey && ref_history.value.canUndo) {
|
||||
|
|
@ -114,30 +114,9 @@ export const useStore = defineStore("form-builder-store", () => {
|
|||
});
|
||||
|
||||
function setup_undo_redo() {
|
||||
data.value = {
|
||||
active_tab: active_tab,
|
||||
layout: layout,
|
||||
selected_field: selected_field,
|
||||
};
|
||||
|
||||
ref_history.value = useDebouncedRefHistory(data, { deep: true, debounce: 100 });
|
||||
ref_history.value = useDebouncedRefHistory(form, { deep: true, debounce: 100 });
|
||||
|
||||
undo_redo_keyboard_event;
|
||||
|
||||
watch(data, (d) => {
|
||||
layout.value = d.layout;
|
||||
active_tab.value = d.active_tab;
|
||||
selected_field.value = d.selected_field;
|
||||
|
||||
if (d.selected_field?.name) {
|
||||
let field = get_field_by_name(
|
||||
layout.value.tabs,
|
||||
"sections",
|
||||
d.selected_field?.name
|
||||
);
|
||||
selected_field.value = field ? field.df : null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function reset_changes() {
|
||||
|
|
@ -240,7 +219,7 @@ export const useStore = defineStore("form-builder-store", () => {
|
|||
let fields = [];
|
||||
let idx = 0;
|
||||
|
||||
let layout_fields = JSON.parse(JSON.stringify(layout.value.tabs));
|
||||
let layout_fields = JSON.parse(JSON.stringify(form.value.layout.tabs));
|
||||
|
||||
layout_fields.forEach((tab, i) => {
|
||||
if (
|
||||
|
|
@ -309,20 +288,18 @@ export const useStore = defineStore("form-builder-store", () => {
|
|||
return {
|
||||
doctype,
|
||||
doc,
|
||||
layout,
|
||||
active_tab,
|
||||
selected_field,
|
||||
form,
|
||||
dirty,
|
||||
read_only,
|
||||
is_customize_form,
|
||||
preview,
|
||||
drag,
|
||||
get_animation,
|
||||
selected,
|
||||
get_docfields,
|
||||
current_tab,
|
||||
selected,
|
||||
get_df,
|
||||
has_standard_field,
|
||||
current_tab,
|
||||
fetch,
|
||||
reset_changes,
|
||||
validate_fields,
|
||||
|
|
|
|||
|
|
@ -349,20 +349,3 @@ export function confirm_dialog(
|
|||
d.show();
|
||||
d.set_message(message);
|
||||
}
|
||||
|
||||
export function get_field_by_name(parent, child, name) {
|
||||
let field = null;
|
||||
parent.every((f) => {
|
||||
if (f.df.name == name) {
|
||||
field = f;
|
||||
return false;
|
||||
}
|
||||
if (child) {
|
||||
let new_child = child == "sections" ? "columns" : child == "columns" ? "fields" : "";
|
||||
field = get_field_by_name(f[child], new_child, name);
|
||||
if (field) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return field;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue