quasar-ui-danx 0.4.10 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/danx.es.js +5954 -5721
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +6 -6
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/index.d.ts +7 -0
- package/index.ts +1 -0
- package/package.json +8 -3
- package/src/components/ActionTable/ActionMenu.vue +26 -31
- package/src/components/ActionTable/ActionTable.vue +4 -1
- package/src/components/ActionTable/Columns/ActionTableColumn.vue +14 -6
- package/src/components/ActionTable/Columns/ActionTableHeaderColumn.vue +63 -42
- package/src/components/ActionTable/Form/ActionForm.vue +55 -0
- package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +11 -5
- package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +1 -0
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +1 -1
- package/src/components/ActionTable/Form/Fields/NumberField.vue +0 -1
- package/src/components/ActionTable/Form/RenderedForm.vue +11 -10
- package/src/components/ActionTable/Form/index.ts +1 -0
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +3 -3
- package/src/components/ActionTable/TableSummaryRow.vue +48 -37
- package/src/components/ActionTable/Toolbars/ActionToolbar.vue +2 -2
- package/src/components/ActionTable/listControls.ts +3 -2
- package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +30 -5
- package/src/components/Utility/Files/FilePreview.vue +72 -12
- package/src/components/Utility/Popovers/PopoverMenu.vue +34 -29
- package/src/config/index.ts +2 -1
- package/src/helpers/FileUpload.ts +59 -8
- package/src/helpers/actions.ts +27 -27
- package/src/helpers/download.ts +8 -2
- package/src/helpers/multiFileUpload.ts +6 -4
- package/src/helpers/objectStore.ts +14 -17
- package/src/helpers/request.ts +12 -0
- package/src/helpers/singleFileUpload.ts +63 -55
- package/src/helpers/utils.ts +9 -0
- package/src/index.ts +1 -0
- package/src/styles/danx.scss +5 -0
- package/src/styles/index.scss +1 -0
- package/src/styles/themes/danx/action-table.scss +24 -13
- package/src/types/actions.d.ts +13 -4
- package/src/types/controls.d.ts +4 -4
- package/src/types/files.d.ts +10 -5
- package/src/types/index.d.ts +0 -1
- package/src/types/requests.d.ts +2 -0
- package/src/types/tables.d.ts +28 -22
- package/src/{vue-plugin.js → vue-plugin.ts} +5 -4
- package/tsconfig.json +1 -0
- package/types/index.d.ts +2 -0
    
        package/index.d.ts
    ADDED
    
    
    
        package/index.ts
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            export * from "./src";
         | 
    
        package/package.json
    CHANGED
    
    | @@ -1,12 +1,13 @@ | |
| 1 1 | 
             
            {
         | 
| 2 2 | 
             
              "name": "quasar-ui-danx",
         | 
| 3 | 
            -
              "version": "0.4. | 
| 3 | 
            +
              "version": "0.4.12",
         | 
| 4 4 | 
             
              "author": "Dan <dan@flytedesk.com>",
         | 
| 5 5 | 
             
              "description": "DanX Vue / Quasar component library",
         | 
| 6 6 | 
             
              "license": "MIT",
         | 
| 7 7 | 
             
              "type": "module",
         | 
| 8 | 
            -
              "main": "dist/danx. | 
| 9 | 
            -
              "module": "dist/danx.es.js",
         | 
| 8 | 
            +
              "main": "./dist/danx.umd.js",
         | 
| 9 | 
            +
              "module": "./dist/danx.es.js",
         | 
| 10 | 
            +
              "types": "types/index.d.ts",
         | 
| 10 11 | 
             
              "scripts": {
         | 
| 11 12 | 
             
                "dev": "cd dev && quasar dev && cd ..",
         | 
| 12 13 | 
             
                "build": "vite build",
         | 
| @@ -17,6 +18,10 @@ | |
| 17 18 | 
             
                "type": "git",
         | 
| 18 19 | 
             
                "url": "https://github.com/flytedan/quasar-ui-danx"
         | 
| 19 20 | 
             
              },
         | 
| 21 | 
            +
              "peerDependencies": {
         | 
| 22 | 
            +
                "quasar": "^2.0.0",
         | 
| 23 | 
            +
                "vue": "^3.4.21"
         | 
| 24 | 
            +
              },
         | 
| 20 25 | 
             
              "devDependencies": {
         | 
| 21 26 | 
             
                "@quasar/extras": "^1.16.4",
         | 
| 22 27 | 
             
                "@types/luxon": "^3.4.2",
         | 
| @@ -3,53 +3,48 @@ | |
| 3 3 | 
             
                class="px-2 flex dx-action-button"
         | 
| 4 4 | 
             
                :items="activeActions"
         | 
| 5 5 | 
             
                :disabled="!hasTarget"
         | 
| 6 | 
            -
                :tooltip="!hasTarget ? tooltip :  | 
| 7 | 
            -
                :loading="isSaving || loading"
         | 
| 6 | 
            +
                :tooltip="!hasTarget ? tooltip : ''"
         | 
| 7 | 
            +
                :loading="isSaving || !!loading"
         | 
| 8 8 | 
             
                :loading-component="loadingComponent"
         | 
| 9 9 | 
             
                @action-item="onAction"
         | 
| 10 10 | 
             
              />
         | 
| 11 11 | 
             
            </template>
         | 
| 12 | 
            -
            <script setup>
         | 
| 12 | 
            +
            <script setup lang="ts">
         | 
| 13 13 | 
             
            import { computed, ref } from "vue";
         | 
| 14 | 
            +
            import { ActionTarget, ResourceAction } from "../../types";
         | 
| 14 15 | 
             
            import { PopoverMenu } from "../Utility";
         | 
| 15 16 |  | 
| 16 | 
            -
            const  | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
                default: "First select records to perform a batch action"
         | 
| 28 | 
            -
              },
         | 
| 29 | 
            -
              loading: Boolean,
         | 
| 30 | 
            -
              loadingComponent: {
         | 
| 31 | 
            -
                type: [Function, Object],
         | 
| 32 | 
            -
                default: undefined
         | 
| 33 | 
            -
              }
         | 
| 17 | 
            +
            const emit = defineEmits(["success"]);
         | 
| 18 | 
            +
            const props = withDefaults(defineProps<{
         | 
| 19 | 
            +
            	actions: ResourceAction[],
         | 
| 20 | 
            +
            	target?: ActionTarget,
         | 
| 21 | 
            +
            	tooltip?: string,
         | 
| 22 | 
            +
            	loading?: boolean,
         | 
| 23 | 
            +
            	loadingComponent?: any
         | 
| 24 | 
            +
            }>(), {
         | 
| 25 | 
            +
            	target: () => [],
         | 
| 26 | 
            +
            	tooltip: "First select records to perform a batch action",
         | 
| 27 | 
            +
            	loadingComponent: undefined
         | 
| 34 28 | 
             
            });
         | 
| 35 29 |  | 
| 36 30 | 
             
            const hasTarget = computed(() => Array.isArray(props.target) ? props.target.length > 0 : !!props.target);
         | 
| 37 31 |  | 
| 38 32 | 
             
            const activeActions = computed(() => props.actions.filter(action => {
         | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 33 | 
            +
            	if (Array.isArray(props.target)) {
         | 
| 34 | 
            +
            		return action.batchEnabled ? action.batchEnabled(props.target) : true;
         | 
| 35 | 
            +
            	}
         | 
| 42 36 |  | 
| 43 | 
            -
             | 
| 37 | 
            +
            	return action.enabled ? action.enabled(props.target) : true;
         | 
| 44 38 | 
             
            }));
         | 
| 45 39 |  | 
| 46 40 | 
             
            const isSaving = ref(false);
         | 
| 47 41 | 
             
            async function onAction(action) {
         | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 42 | 
            +
            	if (!action.trigger) {
         | 
| 43 | 
            +
            		throw new Error("Action must have a trigger function! Make sure you are using useActions() or implement your own trigger function.");
         | 
| 44 | 
            +
            	}
         | 
| 45 | 
            +
            	isSaving.value = true;
         | 
| 46 | 
            +
            	await action.trigger(props.target);
         | 
| 47 | 
            +
            	isSaving.value = false;
         | 
| 48 | 
            +
            	emit("success", action);
         | 
| 54 49 | 
             
            }
         | 
| 55 50 | 
             
            </script>
         | 
| @@ -31,6 +31,7 @@ | |
| 31 31 | 
             
                      :label="label"
         | 
| 32 32 | 
             
                      :item-count="summary?.count || 0"
         | 
| 33 33 | 
             
                      :selected-count="selectedRows.length"
         | 
| 34 | 
            +
                      :sticky-colspan="summaryColSpan"
         | 
| 34 35 | 
             
                      :loading="loadingSummary"
         | 
| 35 36 | 
             
                      :summary="summary"
         | 
| 36 37 | 
             
                      :columns="tableColumns"
         | 
| @@ -87,6 +88,7 @@ export interface Props { | |
| 87 88 | 
             
            	summary: any;
         | 
| 88 89 | 
             
            	columns: TableColumn[];
         | 
| 89 90 | 
             
            	rowsPerPageOptions?: number[];
         | 
| 91 | 
            +
            	summaryColSpan?: number;
         | 
| 90 92 | 
             
            }
         | 
| 91 93 |  | 
| 92 94 | 
             
            const props = withDefaults(defineProps<Props>(), {
         | 
| @@ -94,7 +96,8 @@ const props = withDefaults(defineProps<Props>(), { | |
| 94 96 | 
             
            	pagedItems: null,
         | 
| 95 97 | 
             
            	summary: null,
         | 
| 96 98 | 
             
            	loadingSummary: false,
         | 
| 97 | 
            -
            	rowsPerPageOptions: () => [10, 25, 50, 100]
         | 
| 99 | 
            +
            	rowsPerPageOptions: () => [10, 25, 50, 100],
         | 
| 100 | 
            +
            	summaryColSpan: null
         | 
| 98 101 | 
             
            });
         | 
| 99 102 |  | 
| 100 103 | 
             
            const actionTable = ref(null);
         | 
| @@ -3,13 +3,18 @@ | |
| 3 3 | 
             
                :key="rowProps.key"
         | 
| 4 4 | 
             
                :props="rowProps"
         | 
| 5 5 | 
             
                :style="columnStyle"
         | 
| 6 | 
            +
                class="dx-column"
         | 
| 7 | 
            +
                :class="column.columnClass"
         | 
| 6 8 | 
             
              >
         | 
| 7 9 | 
             
                <div :style="columnStyle">
         | 
| 8 10 | 
             
                  <div
         | 
| 9 11 | 
             
                    class="flex items-center flex-nowrap"
         | 
| 10 | 
            -
                    :class=" | 
| 12 | 
            +
                    :class="wrapClass"
         | 
| 11 13 | 
             
                  >
         | 
| 12 | 
            -
                    <div | 
| 14 | 
            +
                    <div
         | 
| 15 | 
            +
                      v-if="!column.hideContent"
         | 
| 16 | 
            +
                      class="flex-grow overflow-hidden"
         | 
| 17 | 
            +
                    >
         | 
| 13 18 | 
             
                      <a
         | 
| 14 19 | 
             
                        v-if="column.onClick"
         | 
| 15 20 | 
             
                        :class="column.innerClass"
         | 
| @@ -25,6 +30,7 @@ | |
| 25 30 | 
             
                      <div
         | 
| 26 31 | 
             
                        v-else
         | 
| 27 32 | 
             
                        :class="column.innerClass"
         | 
| 33 | 
            +
                        class="dx-column-text"
         | 
| 28 34 | 
             
                      >
         | 
| 29 35 | 
             
                        <RenderVnode
         | 
| 30 36 | 
             
                          v-if="column.vnode"
         | 
| @@ -42,7 +48,8 @@ | |
| 42 48 | 
             
                    </div>
         | 
| 43 49 | 
             
                    <div
         | 
| 44 50 | 
             
                      v-if="column.actionMenu"
         | 
| 45 | 
            -
                      class="flex flex-shrink-0 | 
| 51 | 
            +
                      class="flex flex-shrink-0"
         | 
| 52 | 
            +
                      :class="{'ml-2': !column.hideContent}"
         | 
| 46 53 | 
             
                    >
         | 
| 47 54 | 
             
                      <ActionMenu
         | 
| 48 55 | 
             
                        class="dx-column-action-menu"
         | 
| @@ -55,12 +62,13 @@ | |
| 55 62 | 
             
                </div>
         | 
| 56 63 | 
             
              </QTd>
         | 
| 57 64 | 
             
            </template>
         | 
| 58 | 
            -
            <script setup>
         | 
| 65 | 
            +
            <script setup lang="ts">
         | 
| 59 66 | 
             
            import { QTd } from "quasar";
         | 
| 60 67 | 
             
            import { computed } from "vue";
         | 
| 61 68 | 
             
            import { RenderVnode } from "../../Utility";
         | 
| 62 69 | 
             
            import ActionMenu from "../ActionMenu";
         | 
| 63 70 | 
             
            import { TitleColumnFormat } from "./";
         | 
| 71 | 
            +
            import { TableColumn } from "./../../../types";
         | 
| 64 72 |  | 
| 65 73 | 
             
            const props = defineProps({
         | 
| 66 74 | 
             
            	rowProps: {
         | 
| @@ -74,7 +82,7 @@ const props = defineProps({ | |
| 74 82 | 
             
            });
         | 
| 75 83 |  | 
| 76 84 | 
             
            const row = computed(() => props.rowProps.row);
         | 
| 77 | 
            -
            const column = computed(() => props.rowProps.col);
         | 
| 85 | 
            +
            const column = computed<TableColumn>(() => props.rowProps.col);
         | 
| 78 86 | 
             
            const value = computed(() => props.rowProps.value);
         | 
| 79 87 | 
             
            const isSaving = computed(() => row.value.isSaving?.value);
         | 
| 80 88 |  | 
| @@ -86,7 +94,7 @@ const columnStyle = computed(() => { | |
| 86 94 | 
             
            	};
         | 
| 87 95 | 
             
            });
         | 
| 88 96 |  | 
| 89 | 
            -
            const  | 
| 97 | 
            +
            const wrapClass = computed(() => ({
         | 
| 90 98 | 
             
            	[column.value.class || ""]: true,
         | 
| 91 99 | 
             
            	"is-saving": isSaving.value,
         | 
| 92 100 | 
             
            	"justify-end": column.value.align === "right",
         | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
                :key="rowProps.key"
         | 
| 4 4 | 
             
                :props="rowProps"
         | 
| 5 5 | 
             
                :data-drop-zone="isResizeable && `resize-column-` + column.name"
         | 
| 6 | 
            -
                :class=" | 
| 6 | 
            +
                :class="columnClass"
         | 
| 7 7 | 
             
                :style="columnStyle"
         | 
| 8 8 | 
             
              >
         | 
| 9 9 | 
             
                {{ column.label }}
         | 
| @@ -17,67 +17,88 @@ | |
| 17 17 | 
             
                </HandleDraggable>
         | 
| 18 18 | 
             
              </QTh>
         | 
| 19 19 | 
             
            </template>
         | 
| 20 | 
            -
            <script setup>
         | 
| 20 | 
            +
            <script setup lang="ts">
         | 
| 21 21 | 
             
            import { QTh } from "quasar";
         | 
| 22 | 
            -
            import { computed } from "vue";
         | 
| 22 | 
            +
            import { computed, useCssModule } from "vue";
         | 
| 23 23 | 
             
            import { DragHandleIcon as RowResizeIcon } from "../../../svg";
         | 
| 24 | 
            +
            import { TableColumn } from "../../../types";
         | 
| 24 25 | 
             
            import { HandleDraggable } from "../../DragAndDrop";
         | 
| 25 26 |  | 
| 26 27 | 
             
            const emit = defineEmits(["update:model-value"]);
         | 
| 27 28 | 
             
            const props = defineProps({
         | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 29 | 
            +
            	modelValue: {
         | 
| 30 | 
            +
            		type: Object,
         | 
| 31 | 
            +
            		required: true
         | 
| 32 | 
            +
            	},
         | 
| 33 | 
            +
            	name: {
         | 
| 34 | 
            +
            		type: String,
         | 
| 35 | 
            +
            		required: true
         | 
| 36 | 
            +
            	},
         | 
| 37 | 
            +
            	rowProps: {
         | 
| 38 | 
            +
            		type: Object,
         | 
| 39 | 
            +
            		required: true
         | 
| 40 | 
            +
            	}
         | 
| 40 41 | 
             
            });
         | 
| 41 42 |  | 
| 42 | 
            -
            const column = computed(() => props.rowProps.col);
         | 
| 43 | 
            +
            const column = computed<TableColumn>(() => props.rowProps.col);
         | 
| 43 44 | 
             
            const isResizeable = computed(() => column.value.resizeable);
         | 
| 44 45 |  | 
| 45 46 | 
             
            const columnStyle = computed(() => {
         | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 47 | 
            +
            	const width = props.settings?.width || column.value.width;
         | 
| 48 | 
            +
            	return {
         | 
| 49 | 
            +
            		width: width ? `${width}px` : undefined,
         | 
| 50 | 
            +
            		minWidth: column.value.minWidth ? `${column.value.minWidth}px` : undefined,
         | 
| 51 | 
            +
            		...(column.value.headerStyle || {})
         | 
| 52 | 
            +
            	};
         | 
| 52 53 | 
             
            });
         | 
| 53 54 |  | 
| 55 | 
            +
            const clsModule = useCssModule("cls");
         | 
| 56 | 
            +
            const columnClass = computed(() => {
         | 
| 57 | 
            +
            	const colCls = {
         | 
| 58 | 
            +
            		[clsModule["handle-drop-zone"]]: isResizeable.value,
         | 
| 59 | 
            +
            		"dx-column-shrink": column.value.shrink
         | 
| 60 | 
            +
            	};
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            	const headerClass = column.value.headerClass;
         | 
| 63 | 
            +
            	if (headerClass) {
         | 
| 64 | 
            +
            		if (typeof headerClass === "string") {
         | 
| 65 | 
            +
            			colCls[headerClass] = true;
         | 
| 66 | 
            +
            		} else {
         | 
| 67 | 
            +
            			Object.keys(headerClass).forEach((key) => {
         | 
| 68 | 
            +
            				colCls[key] = headerClass[key];
         | 
| 69 | 
            +
            			});
         | 
| 70 | 
            +
            		}
         | 
| 71 | 
            +
            	}
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            	return colCls;
         | 
| 74 | 
            +
            });
         | 
| 54 75 |  | 
| 55 76 | 
             
            function onResizeColumn(val) {
         | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 77 | 
            +
            	const settings = {
         | 
| 78 | 
            +
            		...props.modelValue,
         | 
| 79 | 
            +
            		[column.value.name]: {
         | 
| 80 | 
            +
            			width: Math.max(Math.min(val.distance + val.startDropZoneSize, column.value.maxWidth || 500), column.value.minWidth || 80)
         | 
| 81 | 
            +
            		}
         | 
| 82 | 
            +
            	};
         | 
| 83 | 
            +
            	emit("update:model-value", settings);
         | 
| 63 84 | 
             
            }
         | 
| 64 85 | 
             
            </script>
         | 
| 65 86 |  | 
| 66 87 | 
             
            <style lang="scss" module="cls">
         | 
| 67 88 | 
             
            .handle-drop-zone {
         | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 89 | 
            +
            	.resize-handle {
         | 
| 90 | 
            +
            		position: absolute;
         | 
| 91 | 
            +
            		top: 0;
         | 
| 92 | 
            +
            		right: -.45em;
         | 
| 93 | 
            +
            		width: .9em;
         | 
| 94 | 
            +
            		opacity: 0;
         | 
| 95 | 
            +
            		transition: all .3s;
         | 
| 96 | 
            +
            	}
         | 
| 76 97 |  | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 98 | 
            +
            	&:hover {
         | 
| 99 | 
            +
            		.resize-handle {
         | 
| 100 | 
            +
            			opacity: 1;
         | 
| 101 | 
            +
            		}
         | 
| 102 | 
            +
            	}
         | 
| 82 103 | 
             
            }
         | 
| 83 104 | 
             
            </style>
         | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            <template>
         | 
| 2 | 
            +
              <div>
         | 
| 3 | 
            +
                <RenderedForm
         | 
| 4 | 
            +
                  v-bind="renderedFormProps"
         | 
| 5 | 
            +
                  v-model:values="input"
         | 
| 6 | 
            +
                  empty-value=""
         | 
| 7 | 
            +
                  :saving="action.isApplying"
         | 
| 8 | 
            +
                  @update:values="action.trigger(target, input)"
         | 
| 9 | 
            +
                />
         | 
| 10 | 
            +
              </div>
         | 
| 11 | 
            +
            </template>
         | 
| 12 | 
            +
            <script setup lang="ts">
         | 
| 13 | 
            +
            import { ref, watch } from "vue";
         | 
| 14 | 
            +
            import { ActionTargetItem, AnyObject, Form, ResourceAction } from "../../../types";
         | 
| 15 | 
            +
            import RenderedForm from "./RenderedForm.vue";
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            interface ActionFormProps {
         | 
| 18 | 
            +
            	action: ResourceAction;
         | 
| 19 | 
            +
            	target: ActionTargetItem;
         | 
| 20 | 
            +
            	form: Form;
         | 
| 21 | 
            +
            	noLabel?: boolean;
         | 
| 22 | 
            +
            	showName?: boolean;
         | 
| 23 | 
            +
            	disable?: boolean;
         | 
| 24 | 
            +
            	readonly?: boolean;
         | 
| 25 | 
            +
            	clearable?: boolean;
         | 
| 26 | 
            +
            	fieldClass?: string;
         | 
| 27 | 
            +
            	savingClass?: string;
         | 
| 28 | 
            +
            }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            const props = defineProps<ActionFormProps>();
         | 
| 31 | 
            +
            const renderedFormProps = {
         | 
| 32 | 
            +
            	form: props.form,
         | 
| 33 | 
            +
            	noLabel: props.noLabel,
         | 
| 34 | 
            +
            	showName: props.showName,
         | 
| 35 | 
            +
            	disable: props.disable,
         | 
| 36 | 
            +
            	readonly: props.readonly,
         | 
| 37 | 
            +
            	clearable: props.clearable,
         | 
| 38 | 
            +
            	fieldClass: props.fieldClass,
         | 
| 39 | 
            +
            	savingClass: props.savingClass
         | 
| 40 | 
            +
            };
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            const input: AnyObject = ref({ ...props.target });
         | 
| 43 | 
            +
            const fieldStatus: AnyObject = {};
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            // Only update field values from target changes when the field is not already being saved
         | 
| 46 | 
            +
            watch(() => props.target, (target: ActionTargetItem) => {
         | 
| 47 | 
            +
            	if (!target) return;
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            	for (let field of props.form.fields) {
         | 
| 50 | 
            +
            		if (!fieldStatus[field.name]?.isSaving) {
         | 
| 51 | 
            +
            			input.value[field.name] = target[field.name];
         | 
| 52 | 
            +
            		}
         | 
| 53 | 
            +
            	}
         | 
| 54 | 
            +
            });
         | 
| 55 | 
            +
            </script>
         | 
| @@ -3,12 +3,13 @@ | |
| 3 3 | 
             
                class="danx-edit-on-click-text-field flex flex-nowrap items-center rounded overflow-ellipsis"
         | 
| 4 4 | 
             
                :class="{[props.class]: true, 'is-readonly': readonly, 'cursor-pointer': !isEditing && !readonly}"
         | 
| 5 5 | 
             
                @click="onEdit"
         | 
| 6 | 
            +
                @focusout="isEditing = false"
         | 
| 6 7 | 
             
              >
         | 
| 7 8 | 
             
                <div
         | 
| 8 9 | 
             
                  ref="editableBox"
         | 
| 9 10 | 
             
                  :contenteditable="!readonly && isEditing"
         | 
| 10 11 | 
             
                  class="flex-grow p-2 rounded outline-none border-none"
         | 
| 11 | 
            -
                  :class="{[editingClass]: isEditing}"
         | 
| 12 | 
            +
                  :class="{[editingClass]: isEditing, [inputClass]: true}"
         | 
| 12 13 | 
             
                  @input="text = $event.target.innerText"
         | 
| 13 14 | 
             
                >
         | 
| 14 15 | 
             
                  {{ text }}
         | 
| @@ -33,15 +34,20 @@ | |
| 33 34 | 
             
            import { FaSolidCheck as DoneIcon, FaSolidPencil as EditIcon } from "danx-icon";
         | 
| 34 35 | 
             
            import { nextTick, ref } from "vue";
         | 
| 35 36 |  | 
| 36 | 
            -
            export interface  | 
| 37 | 
            -
            	class?:  | 
| 38 | 
            -
            	editingClass?:  | 
| 37 | 
            +
            export interface EditOnClickTextFieldProps {
         | 
| 38 | 
            +
            	class?: string;
         | 
| 39 | 
            +
            	editingClass?: string;
         | 
| 40 | 
            +
            	inputClass?: string;
         | 
| 39 41 | 
             
            	readonly?: boolean;
         | 
| 40 42 | 
             
            }
         | 
| 41 43 |  | 
| 42 44 | 
             
            const editableBox = ref<HTMLElement | null>(null);
         | 
| 43 45 | 
             
            const text = defineModel({ type: String });
         | 
| 44 | 
            -
            const props = defineProps< | 
| 46 | 
            +
            const props = withDefaults(defineProps<EditOnClickTextFieldProps>(), {
         | 
| 47 | 
            +
            	class: "hover:bg-slate-300",
         | 
| 48 | 
            +
            	editingClass: "bg-slate-500",
         | 
| 49 | 
            +
            	inputClass: "whitespace-normal"
         | 
| 50 | 
            +
            });
         | 
| 45 51 | 
             
            const isEditing = ref(false);
         | 
| 46 52 | 
             
            function onEdit() {
         | 
| 47 53 | 
             
            	if (props.readonly) return;
         | 
| @@ -62,6 +62,7 @@ function upload() { | |
| 62 62 | 
             
            async function onAttachFiles({ target: { files } }) {
         | 
| 63 63 | 
             
            	emit("uploading", files);
         | 
| 64 64 | 
             
            	let fileUpload = new FileUpload(files)
         | 
| 65 | 
            +
            		.prepare()
         | 
| 65 66 | 
             
            		.onProgress(({ file, progress }) => {
         | 
| 66 67 | 
             
            			file.progress = progress;
         | 
| 67 68 | 
             
            			emit("file-progress", file);
         | 
| @@ -136,7 +136,7 @@ import { ExclamationCircleIcon as MissingIcon, PencilIcon as EditIcon } from "@h | |
| 136 136 | 
             
            import { computed, ref } from "vue";
         | 
| 137 137 | 
             
            import { fDateTime, FlashMessages, incrementName, replace } from "../../../helpers";
         | 
| 138 138 | 
             
            import { TrashIcon as RemoveIcon } from "../../../svg";
         | 
| 139 | 
            -
            import { Form, FormFieldValue } from "../../../types";
         | 
| 139 | 
            +
            import { AnyObject, Form, FormFieldValue } from "../../../types";
         | 
| 140 140 | 
             
            import { ConfirmDialog, RenderVnode } from "../../Utility";
         | 
| 141 141 | 
             
            import {
         | 
| 142 142 | 
             
            	BooleanField,
         | 
| @@ -151,7 +151,7 @@ import { | |
| 151 151 | 
             
            } from "./Fields";
         | 
| 152 152 |  | 
| 153 153 | 
             
            export interface RenderedFormProps {
         | 
| 154 | 
            -
            	values?: FormFieldValue[] | object;
         | 
| 154 | 
            +
            	values?: FormFieldValue[] | object | null;
         | 
| 155 155 | 
             
            	form: Form;
         | 
| 156 156 | 
             
            	noLabel?: boolean;
         | 
| 157 157 | 
             
            	showName?: boolean;
         | 
| @@ -171,7 +171,7 @@ const props = withDefaults(defineProps<RenderedFormProps>(), { | |
| 171 171 | 
             
            	emptyValue: undefined,
         | 
| 172 172 | 
             
            	fieldClass: "",
         | 
| 173 173 | 
             
            	savingClass: "text-sm text-slate-500 justify-end mt-4",
         | 
| 174 | 
            -
            	savedAt:  | 
| 174 | 
            +
            	savedAt: undefined
         | 
| 175 175 | 
             
            });
         | 
| 176 176 |  | 
| 177 177 | 
             
            const emit = defineEmits(["update:values"]);
         | 
| @@ -226,16 +226,16 @@ const currentVariation = ref(variationNames.value[0] || ""); | |
| 226 226 | 
             
            const newVariationName = ref("");
         | 
| 227 227 | 
             
            const variationToEdit = ref<boolean | string>(false);
         | 
| 228 228 | 
             
            const variationToDelete = ref("");
         | 
| 229 | 
            -
            const canAddVariation = computed(() => props.canModifyVariations && !props.readonly && !props.disable && variationNames.value.length < props.form.variations);
         | 
| 229 | 
            +
            const canAddVariation = computed(() => props.canModifyVariations && !props.readonly && !props.disable && variationNames.value.length < (props.form.variations || 0));
         | 
| 230 230 |  | 
| 231 | 
            -
            function getFieldResponse(name, variation | 
| 231 | 
            +
            function getFieldResponse(name: string, variation?: string) {
         | 
| 232 232 | 
             
            	if (!fieldResponses.value) return undefined;
         | 
| 233 233 | 
             
            	return fieldResponses.value.find((fr: FormFieldValue) => fr.variation === (variation !== undefined ? variation : currentVariation.value) && fr.name === name);
         | 
| 234 234 | 
             
            }
         | 
| 235 | 
            -
            function getFieldValue(name) {
         | 
| 235 | 
            +
            function getFieldValue(name: string) {
         | 
| 236 236 | 
             
            	return getFieldResponse(name)?.value;
         | 
| 237 237 | 
             
            }
         | 
| 238 | 
            -
            function onInput(name, value) {
         | 
| 238 | 
            +
            function onInput(name: string, value: any) {
         | 
| 239 239 | 
             
            	const fieldResponse = getFieldResponse(name);
         | 
| 240 240 | 
             
            	const newFieldResponse = {
         | 
| 241 241 | 
             
            		name,
         | 
| @@ -291,11 +291,12 @@ function updateValues(values: FormFieldValue[]) { | |
| 291 291 | 
             
            	let updatedValues: FormFieldValue[] | object = values;
         | 
| 292 292 |  | 
| 293 293 | 
             
            	if (!Array.isArray(props.values)) {
         | 
| 294 | 
            -
            		updatedValues = values.reduce((acc, v) => {
         | 
| 294 | 
            +
            		updatedValues = values.reduce((acc: AnyObject, v) => {
         | 
| 295 295 | 
             
            			acc[v.name] = v.value;
         | 
| 296 296 | 
             
            			return acc;
         | 
| 297 297 | 
             
            		}, {});
         | 
| 298 298 | 
             
            	}
         | 
| 299 | 
            +
             | 
| 299 300 | 
             
            	emit("update:values", updatedValues);
         | 
| 300 301 | 
             
            }
         | 
| 301 302 |  | 
| @@ -311,8 +312,8 @@ function onRemoveVariation(name: string) { | |
| 311 312 | 
             
            	variationToDelete.value = "";
         | 
| 312 313 | 
             
            }
         | 
| 313 314 |  | 
| 314 | 
            -
            function isVariationFormComplete(variation) {
         | 
| 315 | 
            -
            	const requiredGroups = {};
         | 
| 315 | 
            +
            function isVariationFormComplete(variation: string) {
         | 
| 316 | 
            +
            	const requiredGroups: AnyObject = {};
         | 
| 316 317 | 
             
            	return props.form.fields.filter(r => r.required || r.required_group).every((field) => {
         | 
| 317 318 | 
             
            		const fieldResponse = getFieldResponse(field.name, variation);
         | 
| 318 319 | 
             
            		const hasValue = !!fieldResponse && fieldResponse.value !== null;
         | 
| @@ -68,10 +68,10 @@ | |
| 68 68 | 
             
            </template>
         | 
| 69 69 | 
             
            <script setup lang="ts">
         | 
| 70 70 | 
             
            import { computed } from "vue";
         | 
| 71 | 
            -
            import { ActionController,  | 
| 71 | 
            +
            import { ActionController, ActionPanel, FilterGroup, ResourceAction, TableColumn } from "../../../types";
         | 
| 72 72 | 
             
            import { PanelsDrawer } from "../../PanelsDrawer";
         | 
| 73 73 | 
             
            import { PreviousNextControls } from "../../Utility";
         | 
| 74 | 
            -
            import ActionTable from "../ActionTable";
         | 
| 74 | 
            +
            import ActionTable from "../ActionTable.vue";
         | 
| 75 75 | 
             
            import { CollapsableFiltersSidebar } from "../Filters";
         | 
| 76 76 | 
             
            import { ActionToolbar } from "../Toolbars";
         | 
| 77 77 |  | 
| @@ -83,7 +83,7 @@ const props = defineProps<{ | |
| 83 83 | 
             
            	columns: TableColumn[];
         | 
| 84 84 | 
             
            	filters?: FilterGroup[];
         | 
| 85 85 | 
             
            	panels?: ActionPanel[];
         | 
| 86 | 
            -
            	actions?:  | 
| 86 | 
            +
            	actions?: ResourceAction[];
         | 
| 87 87 | 
             
            	exporter?: () => Promise<void>;
         | 
| 88 88 | 
             
            	panelTitleField?: string;
         | 
| 89 89 | 
             
            	tableClass?: string;
         |