@lowdefy/blocks-files 0.0.0-experimental-20260707082859

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.
@@ -0,0 +1,193 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import uploadTheme from '../../schemas/uploadTheme.js';
16
+ export default {
17
+ category: 'input',
18
+ icons: [
19
+ 'AiOutlineUpload'
20
+ ],
21
+ valueType: 'object',
22
+ cssKeys: {
23
+ element: 'The outer block wrapper around the upload button and list.',
24
+ trigger: 'The antd upload trigger (.ant-upload-select) that wraps the button.',
25
+ list: 'The uploaded file list container.',
26
+ item: 'Each uploaded file row in the list.'
27
+ },
28
+ events: {
29
+ onBeforeUpload: {
30
+ description: 'Triggered before a file is uploaded. If an action throws, the upload is cancelled.',
31
+ event: {
32
+ file: 'The file metadata (name, type, size, lastModified, uid, url).'
33
+ }
34
+ },
35
+ onChange: {
36
+ description: 'Triggered when the upload state is changing. With emitFileContent, triggered once the file content has been read, with the block value ({ file, fileList }) as the event, where file includes the base64 content.'
37
+ },
38
+ onProgress: {
39
+ description: 'Triggered when the upload state is in progress.',
40
+ event: {
41
+ file: 'The file being uploaded.',
42
+ fileList: 'The full list of files.'
43
+ }
44
+ },
45
+ onSuccess: {
46
+ description: 'Triggered when the upload state is done uploading.',
47
+ event: {
48
+ file: 'The uploaded file.',
49
+ fileList: 'The full list of files.'
50
+ }
51
+ },
52
+ onRemove: {
53
+ description: 'Triggered when the upload has been removed.',
54
+ event: {
55
+ file: 'The removed file.',
56
+ fileList: 'The full list of files.'
57
+ }
58
+ },
59
+ onError: {
60
+ description: 'Triggered when the upload has failed.',
61
+ event: {
62
+ file: 'The file that failed.',
63
+ fileList: 'The full list of files.'
64
+ }
65
+ },
66
+ onClick: 'Triggered when the upload button is clicked.'
67
+ },
68
+ properties: {
69
+ type: 'object',
70
+ properties: {
71
+ accept: {
72
+ type: 'string',
73
+ description: 'File types accepted by the input. See html file type input accept property at https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept.'
74
+ },
75
+ button: {
76
+ type: 'object',
77
+ description: 'Button block properties. See <a href="/Button">Button</a> for all properties.',
78
+ default: {
79
+ icon: 'UploadOutlined',
80
+ title: 'Upload',
81
+ type: 'default'
82
+ },
83
+ docs: {
84
+ displayType: 'button'
85
+ },
86
+ properties: {
87
+ title: {
88
+ type: 'string',
89
+ description: 'Button title text.'
90
+ },
91
+ icon: {
92
+ type: [
93
+ 'string',
94
+ 'object'
95
+ ],
96
+ description: 'Button icon name or Icon block properties.',
97
+ docs: {
98
+ displayType: 'icon'
99
+ }
100
+ },
101
+ type: {
102
+ type: 'string',
103
+ enum: [
104
+ 'default',
105
+ 'primary',
106
+ 'dashed',
107
+ 'text',
108
+ 'link'
109
+ ],
110
+ default: 'default',
111
+ description: 'Button type.'
112
+ },
113
+ danger: {
114
+ type: 'boolean',
115
+ default: false,
116
+ description: 'Set button style to danger.'
117
+ },
118
+ disabled: {
119
+ type: 'boolean',
120
+ default: false,
121
+ description: 'Disable the button.'
122
+ },
123
+ size: {
124
+ type: 'string',
125
+ enum: [
126
+ 'small',
127
+ 'default',
128
+ 'large'
129
+ ],
130
+ default: 'default',
131
+ description: 'Button size.'
132
+ }
133
+ }
134
+ },
135
+ disabled: {
136
+ type: 'boolean',
137
+ description: 'Disable the file input.'
138
+ },
139
+ emitFileContent: {
140
+ type: 'boolean',
141
+ default: false,
142
+ description: 'Instead of uploading, read the file and emit { name, size, type, content } — content a base64 string — as the block value and onChange event. Use with a CallAPI action to store the file with a server-side write request (e.g. AwsS3PutObject). Replaces uploadPolicyRequestId.'
143
+ },
144
+ maxCount: {
145
+ type: 'number',
146
+ description: 'Maximum number of files that can be uploaded.'
147
+ },
148
+ uploadPolicyRequestId: {
149
+ type: 'string',
150
+ description: 'Id of an upload-policy request (e.g. AwsS3PresignedPostPolicy, GcsSignedPostPolicy, AzureBlobUploadSas) that defines to which storage bucket and how the file should be uploaded. Required unless emitFileContent is true.',
151
+ docs: {
152
+ displayType: 'manual',
153
+ block: {
154
+ id: 'block_properties_uploadPolicyRequestId',
155
+ layout: {
156
+ _global: 'settings_input_layout'
157
+ },
158
+ type: 'Label',
159
+ required: true,
160
+ properties: {
161
+ title: 'uploadPolicyRequestId',
162
+ span: 8,
163
+ align: 'right'
164
+ },
165
+ blocks: [
166
+ {
167
+ id: 'block_properties_uploadPolicyRequestId_text',
168
+ type: 'Markdown',
169
+ style: {
170
+ color: '#8c8c8c'
171
+ },
172
+ properties: {
173
+ content: 'Id of an upload-policy request that defines to which storage bucket and how the file should be uploaded.'
174
+ }
175
+ }
176
+ ]
177
+ }
178
+ }
179
+ },
180
+ showUploadList: {
181
+ type: 'boolean',
182
+ default: true,
183
+ description: 'Whether to show default upload list.'
184
+ },
185
+ singleFile: {
186
+ type: 'boolean',
187
+ default: false,
188
+ description: 'Only allow a single file to be uploaded. Only one file can be selected in the prompt.'
189
+ },
190
+ theme: uploadTheme
191
+ }
192
+ }
193
+ };
@@ -0,0 +1,124 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import React, { useEffect } from 'react';
16
+ import { Upload as AntdUpload, theme as antdTheme } from 'antd';
17
+ import { cn, renderHtml, withBlockDefaults } from '@lowdefy/block-utils';
18
+ import { type } from '@lowdefy/helpers';
19
+ import useFileList from '../utils/useFileList.js';
20
+ import getEmitFileContent from '../utils/getEmitFileContent.js';
21
+ import getUploadRequest from '../utils/getUploadRequest.js';
22
+ import getOnPaste from '../utils/getOnPaste.js';
23
+ import withTheme from '../../withTheme.js';
24
+ import './style.module.css';
25
+ const { Dragger } = AntdUpload;
26
+ const UploadDragger = ({ blockId, classNames = {}, methods, properties, styles = {}, value })=>{
27
+ const [state, loadFileList, setFileList, removeFile, setValue] = useFileList({
28
+ properties,
29
+ methods,
30
+ value
31
+ });
32
+ const emitFileContent = properties.emitFileContent === true;
33
+ const uploadRequest = emitFileContent ? getEmitFileContent({
34
+ methods,
35
+ setFileList
36
+ }) : getUploadRequest({
37
+ methods,
38
+ setFileList
39
+ });
40
+ const onPaste = getOnPaste({
41
+ uploadRequest,
42
+ properties
43
+ });
44
+ useEffect(()=>{
45
+ if (!emitFileContent) {
46
+ methods.registerEvent({
47
+ name: '__getUploadPolicy',
48
+ actions: [
49
+ {
50
+ id: '__getUploadPolicy',
51
+ type: 'Request',
52
+ params: [
53
+ properties.uploadPolicyRequestId
54
+ ]
55
+ }
56
+ ]
57
+ });
58
+ }
59
+ }, []);
60
+ useEffect(()=>{
61
+ if (JSON.stringify(value) !== JSON.stringify(state)) {
62
+ setValue(value);
63
+ }
64
+ }, [
65
+ value
66
+ ]);
67
+ useEffect(()=>{
68
+ methods.registerMethod('uploadFromPaste', async ()=>{
69
+ await onPaste();
70
+ });
71
+ }, [
72
+ onPaste
73
+ ]);
74
+ const { token } = antdTheme.useToken();
75
+ const height = type.isNone(properties.height) ? token.controlHeight : properties.height;
76
+ return /*#__PURE__*/ React.createElement("div", {
77
+ id: blockId,
78
+ className: cn('lf-upload-dragger', classNames.element),
79
+ onPaste: onPaste,
80
+ style: {
81
+ '--lf-dragger-height': type.isNumber(height) ? `${height}px` : height,
82
+ ...styles.element
83
+ }
84
+ }, /*#__PURE__*/ React.createElement(Dragger, {
85
+ accept: properties.accept ?? '*',
86
+ beforeUpload: loadFileList,
87
+ classNames: {
88
+ trigger: classNames.trigger,
89
+ list: classNames.list,
90
+ item: classNames.item
91
+ },
92
+ styles: {
93
+ root: {
94
+ display: 'block'
95
+ },
96
+ trigger: styles.trigger,
97
+ list: styles.list,
98
+ item: styles.item
99
+ },
100
+ customRequest: uploadRequest,
101
+ disabled: properties.disabled,
102
+ fileList: state.fileList,
103
+ maxCount: properties.maxCount,
104
+ multiple: !properties.singleFile,
105
+ onRemove: removeFile,
106
+ showUploadList: properties.showUploadList,
107
+ onChange: ()=>{
108
+ // emitFileContent triggers onChange itself once the content is read,
109
+ // so the file object in the event payload carries the base64 content.
110
+ if (!emitFileContent) {
111
+ methods.triggerEvent({
112
+ name: 'onChange'
113
+ });
114
+ }
115
+ }
116
+ }, /*#__PURE__*/ React.createElement("div", {
117
+ className: cn(classNames.hint),
118
+ style: styles.hint
119
+ }, renderHtml({
120
+ html: properties.title ?? 'Click or drag to add a file.',
121
+ methods
122
+ }))));
123
+ };
124
+ export default withBlockDefaults(withTheme('Upload', UploadDragger));
@@ -0,0 +1,142 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import uploadTheme from '../../schemas/uploadTheme.js';
16
+ export default {
17
+ category: 'input',
18
+ icons: [],
19
+ valueType: 'object',
20
+ cssKeys: {
21
+ element: 'The outer dragger box (height container, background, border, shadow).',
22
+ hint: 'The hint content shown inside the drop area.',
23
+ trigger: 'The antd drag surface (.ant-upload-drag). Target for hover/drag-hover border and background.',
24
+ list: 'The uploaded file list container.',
25
+ item: 'Each uploaded file row in the list.'
26
+ },
27
+ events: {
28
+ onBeforeUpload: {
29
+ description: 'Triggered before a file is uploaded. If an action throws, the upload is cancelled.',
30
+ event: {
31
+ file: 'The file metadata (name, type, size, lastModified, uid, url).'
32
+ }
33
+ },
34
+ onChange: {
35
+ description: 'Triggered when the upload state is changing. With emitFileContent, triggered once the file content has been read, with the block value ({ file, fileList }) as the event, where file includes the base64 content.'
36
+ },
37
+ onProgress: {
38
+ description: 'Triggered when the upload state is in progress.',
39
+ event: {
40
+ file: 'The file being uploaded.',
41
+ fileList: 'The full list of files.'
42
+ }
43
+ },
44
+ onSuccess: {
45
+ description: 'Triggered when the upload state is done uploading.',
46
+ event: {
47
+ file: 'The uploaded file.',
48
+ fileList: 'The full list of files.'
49
+ }
50
+ },
51
+ onRemove: {
52
+ description: 'Triggered when the upload has been removed.',
53
+ event: {
54
+ file: 'The removed file.',
55
+ fileList: 'The full list of files.'
56
+ }
57
+ },
58
+ onError: {
59
+ description: 'Triggered when the upload has failed.',
60
+ event: {
61
+ file: 'The file that failed.',
62
+ fileList: 'The full list of files.'
63
+ }
64
+ }
65
+ },
66
+ properties: {
67
+ type: 'object',
68
+ properties: {
69
+ height: {
70
+ type: [
71
+ 'number',
72
+ 'string'
73
+ ],
74
+ description: 'Height of the dragger area. A number is applied in pixels; a string is passed through as a CSS length (e.g. "300px", "50vh"). Defaults to the antd `controlHeight` theme token. If `style.element.height` is set, it overrides this.'
75
+ },
76
+ title: {
77
+ type: 'string',
78
+ description: 'Title of the file input to be displayed on the draggable area.'
79
+ },
80
+ accept: {
81
+ type: 'string',
82
+ description: 'File types accepted by the input. See html file type input accept property at https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept.'
83
+ },
84
+ disabled: {
85
+ type: 'boolean',
86
+ description: 'Disable the file input.'
87
+ },
88
+ emitFileContent: {
89
+ type: 'boolean',
90
+ default: false,
91
+ description: 'Instead of uploading, read the file and emit { name, size, type, content } — content a base64 string — as the block value and onChange event. Use with a CallAPI action to store the file with a server-side write request (e.g. AwsS3PutObject). Replaces uploadPolicyRequestId.'
92
+ },
93
+ maxCount: {
94
+ type: 'number',
95
+ description: 'Maximum number of files that can be uploaded.'
96
+ },
97
+ uploadPolicyRequestId: {
98
+ type: 'string',
99
+ description: 'Id of an upload-policy request (e.g. AwsS3PresignedPostPolicy, GcsSignedPostPolicy, AzureBlobUploadSas) that defines to which storage bucket and how the file should be uploaded. Required unless emitFileContent is true.',
100
+ docs: {
101
+ displayType: 'manual',
102
+ block: {
103
+ id: 'block_properties_uploadPolicyRequestId',
104
+ layout: {
105
+ _global: 'settings_input_layout'
106
+ },
107
+ type: 'Label',
108
+ required: true,
109
+ properties: {
110
+ title: 'uploadPolicyRequestId',
111
+ span: 8,
112
+ align: 'right'
113
+ },
114
+ blocks: [
115
+ {
116
+ id: 'block_properties_uploadPolicyRequestId_text',
117
+ type: 'Markdown',
118
+ style: {
119
+ color: '#8c8c8c'
120
+ },
121
+ properties: {
122
+ content: 'Id of an upload-policy request that defines to which storage bucket and how the file should be uploaded.'
123
+ }
124
+ }
125
+ ]
126
+ }
127
+ }
128
+ },
129
+ showUploadList: {
130
+ type: 'boolean',
131
+ default: true,
132
+ description: 'Whether to show default upload list.'
133
+ },
134
+ singleFile: {
135
+ type: 'boolean',
136
+ default: false,
137
+ description: 'Only allow a single file to be uploaded. Only one file can be selected in the prompt.'
138
+ },
139
+ theme: uploadTheme
140
+ }
141
+ }
142
+ };
@@ -0,0 +1,30 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ @layer components {
18
+ /* Height applies to the drop zone, not the outer wrapper, so the file list
19
+ (rendered as a sibling of `.ant-upload-drag` inside the Dragger root) can
20
+ flow inline below it instead of overflowing a fixed-height container. */
21
+ :global(.lf-upload-dragger .ant-upload-drag) {
22
+ height: var(--lf-dragger-height);
23
+ }
24
+ /* antd v6 applies `padding: token.padding` to `.ant-upload-drag .ant-upload`,
25
+ which overflows the drag area when the wrapper height is small. Scope the
26
+ override to this block and let users add padding via style.hint. */
27
+ :global(.lf-upload-dragger .ant-upload-drag .ant-upload) {
28
+ padding: 0;
29
+ }
30
+ }
@@ -0,0 +1,126 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import React, { useEffect, useState } from 'react';
16
+ import { cn, renderHtml, withBlockDefaults } from '@lowdefy/block-utils';
17
+ import { Upload as AntdUpload } from 'antd';
18
+ import useFileList from '../utils/useFileList.js';
19
+ import getEmitFileContent from '../utils/getEmitFileContent.js';
20
+ import getUploadRequest from '../utils/getUploadRequest.js';
21
+ import withTheme from '../../withTheme.js';
22
+ const UploadPhoto = ({ blockId, classNames = {}, components: { Icon }, events, methods, properties, styles = {}, value })=>{
23
+ const [state, loadFileList, setFileList, removeFile, setValue] = useFileList({
24
+ properties,
25
+ methods,
26
+ value
27
+ });
28
+ const [loading, setLoading] = useState(false);
29
+ const emitFileContent = properties.emitFileContent === true;
30
+ const uploadRequest = emitFileContent ? getEmitFileContent({
31
+ methods,
32
+ setFileList,
33
+ setLoading
34
+ }) : getUploadRequest({
35
+ methods,
36
+ setFileList,
37
+ setLoading
38
+ });
39
+ useEffect(()=>{
40
+ methods.setValue({
41
+ file: null,
42
+ fileList: []
43
+ });
44
+ if (!emitFileContent) {
45
+ methods.registerEvent({
46
+ name: '__getUploadPolicy',
47
+ actions: [
48
+ {
49
+ id: '__getUploadPolicy',
50
+ type: 'Request',
51
+ params: [
52
+ properties.uploadPolicyRequestId
53
+ ]
54
+ }
55
+ ]
56
+ });
57
+ }
58
+ }, []);
59
+ useEffect(()=>{
60
+ if (JSON.stringify(value) !== JSON.stringify(state)) {
61
+ setValue(value);
62
+ }
63
+ }, [
64
+ value
65
+ ]);
66
+ return /*#__PURE__*/ React.createElement("div", {
67
+ id: blockId,
68
+ className: cn('lf-upload-photo', classNames.element),
69
+ style: styles.element
70
+ }, /*#__PURE__*/ React.createElement(AntdUpload, {
71
+ accept: "image/*",
72
+ beforeUpload: loadFileList,
73
+ classNames: {
74
+ trigger: classNames.trigger,
75
+ list: classNames.list,
76
+ item: classNames.item
77
+ },
78
+ styles: {
79
+ trigger: styles.trigger,
80
+ list: styles.list,
81
+ item: styles.item
82
+ },
83
+ customRequest: uploadRequest,
84
+ disabled: properties.disabled,
85
+ fileList: state.fileList,
86
+ listType: "picture-card",
87
+ maxCount: properties.maxCount,
88
+ multiple: !properties.singleFile,
89
+ onRemove: removeFile,
90
+ showUploadList: properties.showUploadList,
91
+ onChange: ()=>{
92
+ // emitFileContent triggers onChange itself once the content is read,
93
+ // so the file object in the event payload carries the base64 content.
94
+ if (!emitFileContent) {
95
+ methods.triggerEvent({
96
+ name: 'onChange'
97
+ });
98
+ }
99
+ }
100
+ }, /*#__PURE__*/ React.createElement("div", {
101
+ className: "lf-upload-photo-content"
102
+ }, /*#__PURE__*/ React.createElement(Icon, {
103
+ blockId: `${blockId}_icon`,
104
+ classNames: {
105
+ element: cn('lf-upload-photo-icon', classNames.icon)
106
+ },
107
+ events: events,
108
+ properties: {
109
+ name: loading ? 'AiOutlineLoading' : 'AiOutlineCamera',
110
+ size: 24
111
+ },
112
+ styles: {
113
+ element: styles.icon
114
+ }
115
+ }), /*#__PURE__*/ React.createElement("div", {
116
+ className: cn('lf-upload-photo-title', classNames.title),
117
+ style: {
118
+ marginTop: 8,
119
+ ...styles.title
120
+ }
121
+ }, renderHtml({
122
+ html: properties.title ?? 'Upload image',
123
+ methods
124
+ })))));
125
+ };
126
+ export default withBlockDefaults(withTheme('Upload', UploadPhoto));