@lowdefy/blocks-basic 0.0.0-experimental-20260928105334 → 0.0.0-experimental-20260928120643
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/blocks/Anchor/Anchor.js +1 -1
- package/dist/blocks/Anchor/gallery.yaml +14 -14
- package/dist/blocks/Anchor/meta.js +4 -2
- package/dist/blocks/Anchor/tests/Anchor.e2e.yaml +1 -1
- package/dist/blocks/Anchor/tests.yaml +6 -6
- package/dist/blocks/Box/gallery.yaml +9 -9
- package/dist/blocks/ClickableHtml/ClickableHtml.js +9 -28
- package/dist/blocks/ClickableHtml/gallery.yaml +86 -0
- package/dist/blocks/ClickableHtml/meta.js +57 -1
- package/dist/blocks/ClickableHtml/tests/ClickableHtml.e2e.yaml +119 -0
- package/dist/blocks/ClickableHtml/tests.yaml +13 -0
- package/dist/blocks/DangerousHtml/DangerousHtml.js +19 -19
- package/dist/blocks/DangerousHtml/meta.js +13 -1
- package/dist/blocks/Dynamic/meta.js +4 -0
- package/dist/blocks/Html/gallery.yaml +28 -0
- package/dist/blocks/Html/meta.js +23 -1
- package/dist/blocks/Html/tests/Html.e2e.yaml +12 -0
- package/dist/blocks/Icon/Icon.js +21 -1
- package/dist/blocks/Icon/gallery.yaml +158 -64
- package/dist/blocks/Icon/meta.js +11 -4
- package/dist/blocks/Icon/tests/Icon.e2e.yaml +3 -3
- package/dist/blocks/Icon/tests.yaml +7 -7
- package/dist/blocks/Img/meta.js +2 -0
- package/dist/blocks/List/gallery.yaml +11 -11
- package/dist/blocks/PollingTimer/PollingTimer.js +120 -0
- package/dist/blocks/PollingTimer/e2e.js +21 -0
- package/dist/blocks/PollingTimer/gallery.yaml +190 -0
- package/dist/blocks/PollingTimer/meta.js +61 -0
- package/dist/blocks/PollingTimer/validateInterval.js +21 -0
- package/dist/blocks/Span/gallery.yaml +7 -7
- package/dist/blocks.js +1 -0
- package/dist/e2e.js +1 -0
- package/dist/metas.js +1 -0
- package/package.json +8 -5
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
properties:
|
|
69
69
|
title: Link with Icon
|
|
70
70
|
url: https://lowdefy.com
|
|
71
|
-
icon:
|
|
71
|
+
icon: link
|
|
72
72
|
- id: icon_download
|
|
73
73
|
type: Anchor
|
|
74
74
|
layout:
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
properties:
|
|
77
77
|
title: Download
|
|
78
78
|
url: https://lowdefy.com
|
|
79
|
-
icon:
|
|
79
|
+
icon: download
|
|
80
80
|
- id: icon_external
|
|
81
81
|
type: Anchor
|
|
82
82
|
layout:
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
properties:
|
|
85
85
|
title: External Link
|
|
86
86
|
url: https://lowdefy.com
|
|
87
|
-
icon:
|
|
87
|
+
icon: SquareArrowOutUpRight
|
|
88
88
|
- id: icon_mail
|
|
89
89
|
type: Anchor
|
|
90
90
|
layout:
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
properties:
|
|
93
93
|
title: Send Email
|
|
94
94
|
url: https://lowdefy.com
|
|
95
|
-
icon:
|
|
95
|
+
icon: mail
|
|
96
96
|
- id: icon_github
|
|
97
97
|
type: Anchor
|
|
98
98
|
layout:
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
properties:
|
|
101
101
|
title: GitHub
|
|
102
102
|
url: https://github.com/lowdefy/lowdefy
|
|
103
|
-
icon:
|
|
103
|
+
icon: GitBranch
|
|
104
104
|
- id: icon_object
|
|
105
105
|
type: Anchor
|
|
106
106
|
layout:
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
title: Colored Icon
|
|
110
110
|
url: https://lowdefy.com
|
|
111
111
|
icon:
|
|
112
|
-
name:
|
|
112
|
+
name: heart
|
|
113
113
|
color: '#ff4d4f'
|
|
114
114
|
|
|
115
115
|
- title: New Tab
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
title: External Docs
|
|
131
131
|
url: https://docs.lowdefy.com
|
|
132
132
|
newTab: true
|
|
133
|
-
icon:
|
|
133
|
+
icon: SquareArrowOutUpRight
|
|
134
134
|
|
|
135
135
|
- title: Disabled
|
|
136
136
|
blocks:
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
title: Disabled with Icon
|
|
151
151
|
url: https://lowdefy.com
|
|
152
152
|
disabled: true
|
|
153
|
-
icon:
|
|
153
|
+
icon: lock
|
|
154
154
|
- id: enabled_link
|
|
155
155
|
type: Anchor
|
|
156
156
|
layout:
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
title: Enabled Link
|
|
160
160
|
url: https://lowdefy.com
|
|
161
161
|
disabled: false
|
|
162
|
-
icon:
|
|
162
|
+
icon: unlock
|
|
163
163
|
|
|
164
164
|
- title: Aria Label
|
|
165
165
|
blocks:
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
flex: 0 0 auto
|
|
178
178
|
properties:
|
|
179
179
|
url: https://lowdefy.com
|
|
180
|
-
icon:
|
|
180
|
+
icon: home
|
|
181
181
|
ariaLabel: Go to home page
|
|
182
182
|
|
|
183
183
|
- title: Rel Attribute
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
properties:
|
|
210
210
|
title: Go Back
|
|
211
211
|
back: true
|
|
212
|
-
icon:
|
|
212
|
+
icon: arrow-left
|
|
213
213
|
- id: nav_home
|
|
214
214
|
type: Anchor
|
|
215
215
|
layout:
|
|
@@ -217,7 +217,7 @@
|
|
|
217
217
|
properties:
|
|
218
218
|
title: Go Home
|
|
219
219
|
home: true
|
|
220
|
-
icon:
|
|
220
|
+
icon: home
|
|
221
221
|
|
|
222
222
|
- title: Keyboard Shortcuts
|
|
223
223
|
blocks:
|
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
properties:
|
|
229
229
|
title: Documentation
|
|
230
230
|
url: https://docs.lowdefy.com
|
|
231
|
-
icon:
|
|
231
|
+
icon: Book
|
|
232
232
|
events:
|
|
233
233
|
onClick:
|
|
234
234
|
shortcut: mod+shift+d
|
|
@@ -313,4 +313,4 @@
|
|
|
313
313
|
properties:
|
|
314
314
|
title: Large Red Link
|
|
315
315
|
url: https://lowdefy.com
|
|
316
|
-
icon:
|
|
316
|
+
icon: heart
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/ export default {
|
|
16
16
|
category: 'display',
|
|
17
17
|
icons: [
|
|
18
|
-
'
|
|
18
|
+
'loading'
|
|
19
19
|
],
|
|
20
20
|
valueType: null,
|
|
21
21
|
cssKeys: {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
'string',
|
|
66
66
|
'object'
|
|
67
67
|
],
|
|
68
|
-
description:
|
|
68
|
+
description: 'Icon name (a semantic name like `edit`, a Lucide icon name like `Pencil`, or a set-qualified name like `tabler:Pencil`) or properties of an Icon block for anchor icon.',
|
|
69
69
|
docs: {
|
|
70
70
|
displayType: 'icon'
|
|
71
71
|
}
|
|
@@ -76,10 +76,12 @@
|
|
|
76
76
|
},
|
|
77
77
|
href: {
|
|
78
78
|
type: 'string',
|
|
79
|
+
urlKind: 'href',
|
|
79
80
|
description: 'The href to link to when the anchor link is clicked.'
|
|
80
81
|
},
|
|
81
82
|
url: {
|
|
82
83
|
type: 'string',
|
|
84
|
+
urlKind: 'url',
|
|
83
85
|
description: 'External url to link to when the anchor link is clicked.'
|
|
84
86
|
},
|
|
85
87
|
rel: {
|
|
@@ -21,18 +21,18 @@
|
|
|
21
21
|
- id: properties.icon string
|
|
22
22
|
type: Anchor
|
|
23
23
|
properties:
|
|
24
|
-
icon:
|
|
24
|
+
icon: Flame
|
|
25
25
|
- id: properties.icon object
|
|
26
26
|
type: Anchor
|
|
27
27
|
properties:
|
|
28
28
|
title: Click here
|
|
29
29
|
icon:
|
|
30
30
|
color: red
|
|
31
|
-
name:
|
|
31
|
+
name: Rocket
|
|
32
32
|
- id: properties.icon and properties.title
|
|
33
33
|
type: Anchor
|
|
34
34
|
properties:
|
|
35
|
-
icon:
|
|
35
|
+
icon: link
|
|
36
36
|
title: This is the anchor title
|
|
37
37
|
- id: properties.disabled
|
|
38
38
|
type: Anchor
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
- id: properties.icon, properties.title and properties.disabled
|
|
43
43
|
type: Anchor
|
|
44
44
|
properties:
|
|
45
|
-
icon:
|
|
45
|
+
icon: link
|
|
46
46
|
title: This is the anchor title
|
|
47
47
|
disabled: true
|
|
48
48
|
- id: properties.url
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
type: Anchor
|
|
93
93
|
properties:
|
|
94
94
|
title: Click here
|
|
95
|
-
icon:
|
|
95
|
+
icon: link
|
|
96
96
|
style:
|
|
97
97
|
color: green !important
|
|
98
98
|
fontSize: 8
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
loading: true
|
|
104
104
|
properties:
|
|
105
105
|
title: Click here
|
|
106
|
-
icon:
|
|
106
|
+
icon: link
|
|
107
107
|
style:
|
|
108
108
|
color: green !important
|
|
109
109
|
fontSize: 8
|
|
@@ -896,7 +896,7 @@
|
|
|
896
896
|
layout:
|
|
897
897
|
flex: 0 0 auto
|
|
898
898
|
properties:
|
|
899
|
-
name:
|
|
899
|
+
name: Rocket
|
|
900
900
|
size: 32
|
|
901
901
|
color: '#1677ff'
|
|
902
902
|
- id: box_feature_a_title
|
|
@@ -918,7 +918,7 @@
|
|
|
918
918
|
layout:
|
|
919
919
|
flex: 0 0 auto
|
|
920
920
|
properties:
|
|
921
|
-
name:
|
|
921
|
+
name: Shield
|
|
922
922
|
size: 32
|
|
923
923
|
color: '#52c41a'
|
|
924
924
|
- id: box_feature_b_title
|
|
@@ -940,7 +940,7 @@
|
|
|
940
940
|
layout:
|
|
941
941
|
flex: 0 0 auto
|
|
942
942
|
properties:
|
|
943
|
-
name:
|
|
943
|
+
name: Plug
|
|
944
944
|
size: 32
|
|
945
945
|
color: '#722ed1'
|
|
946
946
|
- id: box_feature_c_title
|
|
@@ -1110,7 +1110,7 @@
|
|
|
1110
1110
|
layout:
|
|
1111
1111
|
flex: 0 0 auto
|
|
1112
1112
|
properties:
|
|
1113
|
-
name:
|
|
1113
|
+
name: check-circle
|
|
1114
1114
|
size: 20
|
|
1115
1115
|
color: '#52c41a'
|
|
1116
1116
|
- id: box_inbox_item_1_body
|
|
@@ -1147,7 +1147,7 @@
|
|
|
1147
1147
|
layout:
|
|
1148
1148
|
flex: 0 0 auto
|
|
1149
1149
|
properties:
|
|
1150
|
-
name:
|
|
1150
|
+
name: UserPlus
|
|
1151
1151
|
size: 20
|
|
1152
1152
|
color: '#1677ff'
|
|
1153
1153
|
- id: box_inbox_item_2_body
|
|
@@ -1184,7 +1184,7 @@
|
|
|
1184
1184
|
layout:
|
|
1185
1185
|
flex: 0 0 auto
|
|
1186
1186
|
properties:
|
|
1187
|
-
name:
|
|
1187
|
+
name: warning
|
|
1188
1188
|
size: 20
|
|
1189
1189
|
color: '#faad14'
|
|
1190
1190
|
- id: box_inbox_item_3_body
|
|
@@ -1247,7 +1247,7 @@
|
|
|
1247
1247
|
layout:
|
|
1248
1248
|
flex: 0 0 auto
|
|
1249
1249
|
properties:
|
|
1250
|
-
name:
|
|
1250
|
+
name: Gauge
|
|
1251
1251
|
size: 16
|
|
1252
1252
|
color: '#1677ff'
|
|
1253
1253
|
- id: box_sidebar_text_dashboard
|
|
@@ -1274,7 +1274,7 @@
|
|
|
1274
1274
|
layout:
|
|
1275
1275
|
flex: 0 0 auto
|
|
1276
1276
|
properties:
|
|
1277
|
-
name:
|
|
1277
|
+
name: SquareKanban
|
|
1278
1278
|
size: 16
|
|
1279
1279
|
color: rgba(128, 128, 128, 0.7)
|
|
1280
1280
|
- id: box_sidebar_text_projects
|
|
@@ -1301,7 +1301,7 @@
|
|
|
1301
1301
|
layout:
|
|
1302
1302
|
flex: 0 0 auto
|
|
1303
1303
|
properties:
|
|
1304
|
-
name:
|
|
1304
|
+
name: settings
|
|
1305
1305
|
size: 16
|
|
1306
1306
|
color: rgba(128, 128, 128, 0.7)
|
|
1307
1307
|
- id: box_sidebar_text_settings
|
|
@@ -14,32 +14,14 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import React from 'react';
|
|
16
16
|
import { withBlockDefaults, HtmlComponent } from '@lowdefy/block-utils';
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// target in the markup has its own action chain. Its other data-*
|
|
26
|
-
// attributes are the event object.
|
|
27
|
-
function onClick(clickEvent) {
|
|
28
|
-
const target = clickEvent.target.closest('[data-event]');
|
|
29
|
-
if (!target || !clickEvent.currentTarget.contains(target)) return;
|
|
30
|
-
const { event: name, ...data } = target.dataset;
|
|
31
|
-
if (!name) return;
|
|
32
|
-
clickEvent.preventDefault();
|
|
33
|
-
const event = {};
|
|
34
|
-
Object.keys(data).forEach((key)=>{
|
|
35
|
-
event[toSnakeCase(key)] = data[key];
|
|
36
|
-
});
|
|
37
|
-
methods.triggerEvent({
|
|
38
|
-
name,
|
|
39
|
-
event
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
return /*#__PURE__*/ React.createElement(HtmlComponent, {
|
|
17
|
+
// Each clickable element names the event it fires in its data-event attribute
|
|
18
|
+
// (data-event="onEditClick" → events.onEditClick), so every target in the
|
|
19
|
+
// markup has its own action chain. Its other data-* attributes are the event
|
|
20
|
+
// object. HtmlComponent finds the target, including inside data-popover
|
|
21
|
+
// content, and prevents the element's default action. Only the events listed
|
|
22
|
+
// in properties.dataEvents fire, since the markup can hold data.
|
|
23
|
+
const ClickableHtml = ({ blockId, classNames, events, properties, methods, styles })=>/*#__PURE__*/ React.createElement(HtmlComponent, {
|
|
24
|
+
dataEvents: properties.dataEvents,
|
|
43
25
|
div: true,
|
|
44
26
|
events: events,
|
|
45
27
|
html: properties.html,
|
|
@@ -47,7 +29,6 @@ const ClickableHtml = ({ blockId, classNames, events, properties, methods, style
|
|
|
47
29
|
methods: methods,
|
|
48
30
|
className: classNames?.element,
|
|
49
31
|
style: styles?.element,
|
|
50
|
-
|
|
32
|
+
onDataEvent: methods.triggerEvent
|
|
51
33
|
});
|
|
52
|
-
};
|
|
53
34
|
export default withBlockDefaults(ClickableHtml);
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
- id: buttons_basic
|
|
17
17
|
type: ClickableHtml
|
|
18
18
|
properties:
|
|
19
|
+
dataEvents:
|
|
20
|
+
- onSmallClick
|
|
21
|
+
- onLargeClick
|
|
19
22
|
html: '<p>Pick a size: <button data-event="onSmallClick">Small</button> <button data-event="onLargeClick">Large</button></p>'
|
|
20
23
|
events:
|
|
21
24
|
onSmallClick:
|
|
@@ -44,6 +47,8 @@
|
|
|
44
47
|
- id: list_rows
|
|
45
48
|
type: ClickableHtml
|
|
46
49
|
properties:
|
|
50
|
+
dataEvents:
|
|
51
|
+
- onRowClick
|
|
47
52
|
html: '<ul style="list-style: none; padding: 0; margin: 0;"><li data-event="onRowClick" data-record-id="rec_1" style="padding: 8px; cursor: pointer; border-bottom: 1px solid var(--ant-color-border);">First record</li><li data-event="onRowClick" data-record-id="rec_2" style="padding: 8px; cursor: pointer; border-bottom: 1px solid var(--ant-color-border);">Second record</li><li data-event="onRowClick" data-record-id="rec_3" style="padding: 8px; cursor: pointer;">Third record</li></ul>'
|
|
48
53
|
events:
|
|
49
54
|
onRowClick:
|
|
@@ -69,6 +74,8 @@
|
|
|
69
74
|
- id: mixed_content
|
|
70
75
|
type: ClickableHtml
|
|
71
76
|
properties:
|
|
77
|
+
dataEvents:
|
|
78
|
+
- onCommentClick
|
|
72
79
|
html: '<p>This <a href="https://lowdefy.com" target="_blank">link</a> navigates normally, but <a href="#" data-event="onCommentClick" data-thread-id="t_9">this one</a> fires onCommentClick instead.</p>'
|
|
73
80
|
events:
|
|
74
81
|
onCommentClick:
|
|
@@ -99,6 +106,9 @@
|
|
|
99
106
|
border: 1px solid var(--ant-color-border)
|
|
100
107
|
background: var(--ant-color-bg-container)
|
|
101
108
|
properties:
|
|
109
|
+
dataEvents:
|
|
110
|
+
- onEditClick
|
|
111
|
+
- onRemoveClick
|
|
102
112
|
html: '<h4 style="margin: 0 0 8px 0;">Card</h4><p style="margin: 0 0 8px 0; color: var(--ant-color-text-secondary);">Yours to change or remove until tomorrow.</p><button data-event="onEditClick">Edit</button> <button data-event="onRemoveClick">Remove</button>'
|
|
103
113
|
events:
|
|
104
114
|
onEditClick:
|
|
@@ -111,3 +121,79 @@
|
|
|
111
121
|
type: SetState
|
|
112
122
|
params:
|
|
113
123
|
style_card_action: remove
|
|
124
|
+
|
|
125
|
+
- title: Clickable icons and a popover menu
|
|
126
|
+
blocks:
|
|
127
|
+
- id: row_actions
|
|
128
|
+
type: ClickableHtml
|
|
129
|
+
properties:
|
|
130
|
+
dataEvents:
|
|
131
|
+
- onEdit
|
|
132
|
+
- onDuplicate
|
|
133
|
+
- name: onDelete
|
|
134
|
+
confirm: Delete invoice INV-042?
|
|
135
|
+
html: '<p>Invoice INV-042 <i data-icon="edit" data-event="onEdit" data-id="42" data-tooltip="Edit" style="cursor: pointer"></i> <span data-popover="more" style="cursor: pointer"><i data-icon="more-vertical"></i></span></p><div data-popover-content="more" hidden><p data-event="onDuplicate" data-id="42" style="cursor: pointer; margin: 0 0 8px"><i data-icon="copy"></i> Duplicate</p><p data-event="onDelete" data-id="42" style="cursor: pointer; margin: 0; color: var(--ant-color-error)"><i data-icon="delete"></i> Delete</p></div>'
|
|
136
|
+
events:
|
|
137
|
+
onEdit:
|
|
138
|
+
- id: set_edit
|
|
139
|
+
type: SetState
|
|
140
|
+
params:
|
|
141
|
+
row_actions_last: { _string.concat: ['edit ', { _event: id }] }
|
|
142
|
+
onDuplicate:
|
|
143
|
+
- id: set_duplicate
|
|
144
|
+
type: SetState
|
|
145
|
+
params:
|
|
146
|
+
row_actions_last: { _string.concat: ['duplicate ', { _event: id }] }
|
|
147
|
+
onDelete:
|
|
148
|
+
- id: set_delete
|
|
149
|
+
type: SetState
|
|
150
|
+
params:
|
|
151
|
+
row_actions_last: { _string.concat: ['delete ', { _event: id }] }
|
|
152
|
+
- id: row_actions_result
|
|
153
|
+
type: Html
|
|
154
|
+
properties:
|
|
155
|
+
html:
|
|
156
|
+
_string.concat:
|
|
157
|
+
- '<p>Last action: <code>'
|
|
158
|
+
- _state:
|
|
159
|
+
key: row_actions_last
|
|
160
|
+
default: 'none yet'
|
|
161
|
+
- '</code></p>'
|
|
162
|
+
|
|
163
|
+
- title: Confirm before an event
|
|
164
|
+
blocks:
|
|
165
|
+
- id: confirm_actions
|
|
166
|
+
type: ClickableHtml
|
|
167
|
+
properties:
|
|
168
|
+
dataEvents:
|
|
169
|
+
- name: onDelete
|
|
170
|
+
confirm: true
|
|
171
|
+
- onArchive
|
|
172
|
+
html: '<p>INV-2026-0042 <i data-icon="delete" data-event="onDelete" data-id="42" data-tooltip="Delete" data-confirm="Delete invoice INV-2026-0042?" style="cursor: pointer; color: var(--ant-color-error)"></i> <span data-event="onArchive" data-id="42" data-confirm style="cursor: pointer; color: var(--ant-color-primary)">Archive</span></p>'
|
|
173
|
+
events:
|
|
174
|
+
onDelete:
|
|
175
|
+
- id: deleted
|
|
176
|
+
type: SetState
|
|
177
|
+
params:
|
|
178
|
+
confirm_last:
|
|
179
|
+
_string.concat:
|
|
180
|
+
- 'deleted '
|
|
181
|
+
- _event: id
|
|
182
|
+
onArchive:
|
|
183
|
+
- id: archived
|
|
184
|
+
type: SetState
|
|
185
|
+
params:
|
|
186
|
+
confirm_last:
|
|
187
|
+
_string.concat:
|
|
188
|
+
- 'archived '
|
|
189
|
+
- _event: id
|
|
190
|
+
- id: confirm_result
|
|
191
|
+
type: Html
|
|
192
|
+
properties:
|
|
193
|
+
html:
|
|
194
|
+
_string.concat:
|
|
195
|
+
- '<p>Last action: <code>'
|
|
196
|
+
- _state:
|
|
197
|
+
key: confirm_last
|
|
198
|
+
default: 'none yet'
|
|
199
|
+
- '</code></p>'
|
|
@@ -19,8 +19,26 @@
|
|
|
19
19
|
cssKeys: {
|
|
20
20
|
element: 'The Html element.'
|
|
21
21
|
},
|
|
22
|
+
hazards: [
|
|
23
|
+
{
|
|
24
|
+
id: 'html-style-stripped',
|
|
25
|
+
message: 'Content is sanitised with DOMPurify, so <style>, <script> and inline event handlers are removed before render. Clicks are wired through data-event attributes instead of inline handlers.',
|
|
26
|
+
see: 'display-blocks/clickablehtml'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'clickable-html-data-events',
|
|
30
|
+
message: 'List every event the HTML fires in properties.dataEvents: a data-event whose name is not listed does nothing. HTML built from user or request data must escape that data, so the data cannot add data-event elements of its own: _nunjucks escapes {{ values }} unless they are marked safe, while _string.concat and _js do not escape.',
|
|
31
|
+
see: 'display-blocks/clickablehtml'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'clickable-html-confirm-destructive',
|
|
35
|
+
message: 'Give an event that deletes or changes data confirm in dataEvents ({ name: onDelete, confirm: "Delete this row?" }), so every click on it asks first and only OK fires it. data-confirm="Delete this row?" on the element asks too, but only where the markup says so.',
|
|
36
|
+
see: 'concepts/html-attributes'
|
|
37
|
+
}
|
|
38
|
+
],
|
|
22
39
|
// Each clickable element names its event in a data-event attribute, so the
|
|
23
40
|
// event names this block fires are authored in its html.
|
|
41
|
+
dynamicEvents: true,
|
|
24
42
|
events: {
|
|
25
43
|
onTextSelection: 'Trigger action when text is selected and pass selected text to the event object.'
|
|
26
44
|
},
|
|
@@ -28,9 +46,47 @@
|
|
|
28
46
|
type: 'object',
|
|
29
47
|
additionalProperties: false,
|
|
30
48
|
properties: {
|
|
49
|
+
dataEvents: {
|
|
50
|
+
type: 'array',
|
|
51
|
+
description: 'The events the HTML may fire. A data-event whose name is not listed does nothing, and a click on it logs a console warning; with no dataEvents the HTML fires no events. List an event by name, or as { name, confirm } to make every click on it ask first.',
|
|
52
|
+
items: {
|
|
53
|
+
oneOf: [
|
|
54
|
+
{
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'An event name, as written in data-event.'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'object',
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
required: [
|
|
62
|
+
'name'
|
|
63
|
+
],
|
|
64
|
+
properties: {
|
|
65
|
+
name: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: 'An event name, as written in data-event.'
|
|
68
|
+
},
|
|
69
|
+
confirm: {
|
|
70
|
+
oneOf: [
|
|
71
|
+
{
|
|
72
|
+
type: 'boolean',
|
|
73
|
+
description: 'Ask before the event fires, whatever the HTML says, with the element\'s data-confirm message or "Are you sure?".'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
type: 'string',
|
|
77
|
+
minLength: 1,
|
|
78
|
+
description: 'Ask before the event fires, whatever the HTML says, with this message.'
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
},
|
|
31
87
|
html: {
|
|
32
88
|
type: 'string',
|
|
33
|
-
description: 'Content to be rendered as Html. An element with a data-event attribute fires the event it names when clicked (data-event="onEditClick" fires events.onEditClick), and its default browser action is prevented. The event object holds the element\'s other data-* attributes with snake_case keys, so data-event="onEditClick" data-record-id="42" gives { record_id: "42" }.',
|
|
89
|
+
description: 'Content to be rendered as Html. An element with a data-event attribute fires the event it names when clicked (data-event="onEditClick" fires events.onEditClick), if dataEvents lists it, and its default browser action is prevented. The event object holds the element\'s other data-* attributes with snake_case keys, so data-event="onEditClick" data-record-id="42" gives { record_id: "42" }. Targets that are not links or buttons become keyboard focusable, and Enter or Space clicks them. A data-event inside popover content fires too, then closes the popover. A link with data-event fires the event and does not navigate. data-confirm="Delete this row?" on a data-event element asks first: only OK fires the event (bare data-confirm asks "Are you sure?"). Escape user and request data in the HTML, so it cannot add data-event elements of its own. All Html block attributes work too: data-icon, data-tooltip, data-popover, data-page-id links, data-new-tab, data-tag, data-status, data-time, data-format, data-avatar, data-copy, data-truncate and data-tone. See the HTML attributes docs page.',
|
|
34
90
|
docs: {
|
|
35
91
|
displayType: 'text-area'
|
|
36
92
|
}
|
|
@@ -18,6 +18,9 @@ blocks:
|
|
|
18
18
|
- id: clickable_basic
|
|
19
19
|
type: ClickableHtml
|
|
20
20
|
properties:
|
|
21
|
+
dataEvents:
|
|
22
|
+
- onGreetClick
|
|
23
|
+
- onWaveClick
|
|
21
24
|
html: '<p>Hello <button data-event="onGreetClick" data-user-id="u_1">World</button> <button data-event="onWaveClick" data-user-id="u_2">Wave</button></p>'
|
|
22
25
|
events:
|
|
23
26
|
onGreetClick:
|
|
@@ -67,3 +70,119 @@ blocks:
|
|
|
67
70
|
type: ClickableHtml
|
|
68
71
|
properties:
|
|
69
72
|
html: <p>Safe content</p><script>alert('xss')</script>
|
|
73
|
+
|
|
74
|
+
- id: clickable_confirm
|
|
75
|
+
type: ClickableHtml
|
|
76
|
+
properties:
|
|
77
|
+
dataEvents:
|
|
78
|
+
- onDelete
|
|
79
|
+
html: '<button id="confirm_delete" data-event="onDelete" data-row-id="7" data-confirm="Delete row 7?">Delete</button> <button id="proto_event" data-event="__proto__">Proto</button>'
|
|
80
|
+
events:
|
|
81
|
+
onDelete:
|
|
82
|
+
- id: count_delete
|
|
83
|
+
type: SetState
|
|
84
|
+
params:
|
|
85
|
+
delete_count:
|
|
86
|
+
_sum:
|
|
87
|
+
- _state:
|
|
88
|
+
key: delete_count
|
|
89
|
+
default: 0
|
|
90
|
+
- 1
|
|
91
|
+
|
|
92
|
+
- id: clickable_confirm_result
|
|
93
|
+
type: Html
|
|
94
|
+
properties:
|
|
95
|
+
html:
|
|
96
|
+
_string.concat:
|
|
97
|
+
- '<span id="delete_count">'
|
|
98
|
+
- _json.stringify:
|
|
99
|
+
- _state:
|
|
100
|
+
key: delete_count
|
|
101
|
+
default: 0
|
|
102
|
+
- '</span>'
|
|
103
|
+
|
|
104
|
+
- id: clickable_allowlist
|
|
105
|
+
type: ClickableHtml
|
|
106
|
+
properties:
|
|
107
|
+
dataEvents:
|
|
108
|
+
- onListedClick
|
|
109
|
+
html: '<span id="listed_target" data-event="onListedClick">Listed</span> <span id="unlisted_target" data-event="onUnlistedClick">Unlisted</span>'
|
|
110
|
+
events:
|
|
111
|
+
onListedClick:
|
|
112
|
+
- id: set_listed
|
|
113
|
+
type: SetState
|
|
114
|
+
params:
|
|
115
|
+
allowlist_fired: listed
|
|
116
|
+
onUnlistedClick:
|
|
117
|
+
- id: set_unlisted
|
|
118
|
+
type: SetState
|
|
119
|
+
params:
|
|
120
|
+
allowlist_fired: unlisted
|
|
121
|
+
|
|
122
|
+
- id: clickable_declared_confirm
|
|
123
|
+
type: ClickableHtml
|
|
124
|
+
properties:
|
|
125
|
+
dataEvents:
|
|
126
|
+
- name: onRemove
|
|
127
|
+
confirm: Remove row 3?
|
|
128
|
+
html: '<button id="declared_remove" data-event="onRemove" data-row-id="3">Remove</button>'
|
|
129
|
+
events:
|
|
130
|
+
onRemove:
|
|
131
|
+
- id: set_removed
|
|
132
|
+
type: SetState
|
|
133
|
+
params:
|
|
134
|
+
removed_row:
|
|
135
|
+
_event: row_id
|
|
136
|
+
|
|
137
|
+
- id: clickable_refresh
|
|
138
|
+
type: ClickableHtml
|
|
139
|
+
properties:
|
|
140
|
+
dataEvents:
|
|
141
|
+
- onRefreshSoon
|
|
142
|
+
- onRefreshDelete
|
|
143
|
+
html:
|
|
144
|
+
_string.concat:
|
|
145
|
+
- '<button id="refresh_soon" data-event="onRefreshSoon">Refresh soon</button><p id="refresh_count">Refreshed '
|
|
146
|
+
- _state:
|
|
147
|
+
key: refresh_count
|
|
148
|
+
default: 0
|
|
149
|
+
- '</p><button id="refresh_delete" data-event="onRefreshDelete" data-id="5" data-confirm="Delete 5?">Delete</button> <span id="refresh_more" data-popover="more">More</span><div data-popover-content="more" hidden><span class="refresh-menu">Menu after '
|
|
150
|
+
- _state:
|
|
151
|
+
key: refresh_count
|
|
152
|
+
default: 0
|
|
153
|
+
- '</span></div>'
|
|
154
|
+
events:
|
|
155
|
+
onRefreshSoon:
|
|
156
|
+
- id: wait
|
|
157
|
+
type: Wait
|
|
158
|
+
params:
|
|
159
|
+
ms: 1000
|
|
160
|
+
- id: refresh
|
|
161
|
+
type: SetState
|
|
162
|
+
params:
|
|
163
|
+
refresh_count: 1
|
|
164
|
+
onRefreshDelete:
|
|
165
|
+
- id: set_refresh_deleted
|
|
166
|
+
type: SetState
|
|
167
|
+
params:
|
|
168
|
+
refresh_deleted:
|
|
169
|
+
_event: id
|
|
170
|
+
|
|
171
|
+
- id: clickable_events_result
|
|
172
|
+
type: Html
|
|
173
|
+
properties:
|
|
174
|
+
html:
|
|
175
|
+
_string.concat:
|
|
176
|
+
- '<span id="allowlist_fired">'
|
|
177
|
+
- _state:
|
|
178
|
+
key: allowlist_fired
|
|
179
|
+
default: ''
|
|
180
|
+
- '</span><span id="removed_row">'
|
|
181
|
+
- _state:
|
|
182
|
+
key: removed_row
|
|
183
|
+
default: ''
|
|
184
|
+
- '</span><span id="refresh_deleted">'
|
|
185
|
+
- _state:
|
|
186
|
+
key: refresh_deleted
|
|
187
|
+
default: ''
|
|
188
|
+
- '</span>'
|