diff --git a/packages/builder/src/components/design/AppPreview/componentStructure.json b/packages/builder/src/components/design/AppPreview/componentStructure.json index 5385a8852b..0b888ba10b 100644 --- a/packages/builder/src/components/design/AppPreview/componentStructure.json +++ b/packages/builder/src/components/design/AppPreview/componentStructure.json @@ -44,7 +44,8 @@ "relationshipfield", "daterangepicker", "multifieldselect", - "jsonfield" + "jsonfield", + "s3upload" ] }, { diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/S3Upload.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/S3Upload.svelte new file mode 100644 index 0000000000..76cccf58c5 --- /dev/null +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/S3Upload.svelte @@ -0,0 +1,33 @@ + + +
+ + diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js index e752240302..dea7ca95b7 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js @@ -1,5 +1,6 @@ import { Checkbox, Select, Stepper } from "@budibase/bbui" import DataSourceSelect from "./DataSourceSelect.svelte" +import S3DataSourceSelect from "./S3DataSourceSelect.svelte" import DataProviderSelect from "./DataProviderSelect.svelte" import ButtonActionEditor from "./ButtonActionEditor/ButtonActionEditor.svelte" import TableSelect from "./TableSelect.svelte" @@ -21,6 +22,7 @@ const componentMap = { text: DrawerBindableCombobox, select: Select, dataSource: DataSourceSelect, + "dataSource/s3": S3DataSourceSelect, dataProvider: DataProviderSelect, boolean: Checkbox, number: Stepper, diff --git a/packages/client/manifest.json b/packages/client/manifest.json index dfe9ae5a91..0607eb50d7 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -3315,5 +3315,44 @@ "suffix": "repeater" } ] + }, + "s3upload": { + "name": "S3 File Upload", + "icon": "UploadToCloud", + "styles": ["size"], + "editable": true, + "settings": [ + { + "type": "field/attachment", + "label": "Field", + "key": "field" + }, + { + "type": "text", + "label": "Label", + "key": "label" + }, + { + "type": "dataSource/s3", + "label": "S3 Datasource", + "key": "datasource" + }, + { + "type": "text", + "label": "Bucket", + "key": "bucket" + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false + }, + { + "type": "validation/attachment", + "label": "Validation", + "key": "validation" + } + ] } } diff --git a/packages/client/src/components/app/forms/S3Upload.svelte b/packages/client/src/components/app/forms/S3Upload.svelte new file mode 100644 index 0000000000..645736838b --- /dev/null +++ b/packages/client/src/components/app/forms/S3Upload.svelte @@ -0,0 +1,79 @@ + + + + {#if fieldState} + { + fieldApi.setValue(e.detail) + }} + {processFiles} + {handleFileTooLarge} + maximum={1} + fileSizeLimit={MaxFileSize} + /> + {/if} + diff --git a/packages/client/src/components/app/forms/index.js b/packages/client/src/components/app/forms/index.js index ab1f7d20ed..0ff82cea94 100644 --- a/packages/client/src/components/app/forms/index.js +++ b/packages/client/src/components/app/forms/index.js @@ -12,3 +12,4 @@ export { default as relationshipfield } from "./RelationshipField.svelte" export { default as passwordfield } from "./PasswordField.svelte" export { default as formstep } from "./FormStep.svelte" export { default as jsonfield } from "./JSONField.svelte" +export { default as s3upload } from "./S3Upload.svelte" diff --git a/packages/client/src/sdk.js b/packages/client/src/sdk.js index ab1150658f..42c2057ca6 100644 --- a/packages/client/src/sdk.js +++ b/packages/client/src/sdk.js @@ -5,6 +5,7 @@ import { routeStore, screenStore, builderStore, + uploadStore, } from "stores" import { styleable } from "utils/styleable" import { linkable } from "utils/linkable" @@ -19,6 +20,7 @@ export default { routeStore, screenStore, builderStore, + uploadStore, styleable, linkable, getAction, diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index 02c848120c..9f6e5f6f50 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -9,6 +9,7 @@ export { confirmationStore } from "./confirmation" export { peekStore } from "./peek" export { stateStore } from "./state" export { themeStore } from "./theme" +export { uploadStore } from "./uploads.js" // Context stores are layered and duplicated, so it is not a singleton export { createContextStore } from "./context" diff --git a/packages/client/src/stores/uploads.js b/packages/client/src/stores/uploads.js new file mode 100644 index 0000000000..419d1e1c11 --- /dev/null +++ b/packages/client/src/stores/uploads.js @@ -0,0 +1,42 @@ +import { writable, get } from "svelte/store" + +export const createUploadStore = () => { + const store = writable([]) + + // Registers a new file upload component + const registerFileUpload = (componentId, callback) => { + if (!componentId || !callback) { + return + } + + store.update(state => { + state.push({ + componentId, + callback, + }) + return state + }) + } + + // Unregisters a file upload component + const unregisterFileUpload = componentId => { + store.update(state => state.filter(c => c.componentId !== componentId)) + } + + // Processes a file upload for a given component ID + const processFileUpload = async componentId => { + if (!componentId) { + return + } + + const component = get(store).find(c => c.componentId === componentId) + await component?.callback() + } + + return { + subscribe: store.subscribe, + actions: { registerFileUpload, unregisterFileUpload, processFileUpload }, + } +} + +export const uploadStore = createUploadStore() diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 6b4dd4235a..6085fafec5 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -5,6 +5,7 @@ import { confirmationStore, authStore, stateStore, + uploadStore, } from "stores" import { saveRow, deleteRow, executeQuery, triggerAutomation } from "api" import { ActionTypes } from "constants" @@ -157,6 +158,14 @@ const updateStateHandler = action => { } } +const s3UploadHandler = async action => { + const { componentId } = action.parameters + if (!componentId) { + return + } + await uploadStore.actions.processFileUpload(componentId) +} + const handlerMap = { ["Save Row"]: saveRowHandler, ["Duplicate Row"]: duplicateRowHandler, @@ -171,6 +180,7 @@ const handlerMap = { ["Close Screen Modal"]: closeScreenModalHandler, ["Change Form Step"]: changeFormStepHandler, ["Update State"]: updateStateHandler, + ["Upload File to S3"]: s3UploadHandler, } const confirmTextMap = {