@lowdefy/docs-content 0.0.0-experimental-20260928120643 → 6.1.0
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/README.md +0 -1
- package/content/actions/link.md +0 -17
- package/content/actions/login.md +5 -23
- package/content/actions/logout.md +5 -5
- package/content/actions/pdfmake.md +2 -2
- package/content/actions/setdarkmode.md +2 -2
- package/content/agents/agent-properties.md +2 -4
- package/content/agents/agentchat.md +6 -21
- package/content/agents/agentconversations.md +2 -2
- package/content/concepts/ai-agent-docs.md +101 -313
- package/content/concepts/cli.md +3 -34
- package/content/concepts/connections-and-requests.md +0 -402
- package/content/concepts/custom-html.md +0 -40
- package/content/concepts/custom-styling.md +5 -50
- package/content/concepts/events-and-actions.md +2 -64
- package/content/concepts/i18n.md +2 -7
- package/content/concepts/keyboard-shortcuts.md +2 -2
- package/content/concepts/layout-overview.md +1 -1
- package/content/concepts/logger.md +0 -81
- package/content/concepts/lowdefy-schema.md +1 -4
- package/content/concepts/menus.md +7 -7
- package/content/concepts/module-authoring.md +2 -2
- package/content/connections/anthropic.md +2 -162
- package/content/connections/awss3.md +0 -54
- package/content/connections/google.md +2 -162
- package/content/connections/mongodb.md +3 -121
- package/content/connections/openai-connection.md +2 -162
- package/content/container-blocks/affix.md +7 -47
- package/content/container-blocks/alert.md +24 -76
- package/content/container-blocks/badge.md +24 -100
- package/content/container-blocks/box.md +9 -9
- package/content/container-blocks/card.md +25 -80
- package/content/container-blocks/carousel.md +7 -60
- package/content/container-blocks/collapse.md +9 -87
- package/content/container-blocks/configprovider.md +13 -35
- package/content/container-blocks/confirmmodal.md +23 -69
- package/content/container-blocks/content.md +98 -62
- package/content/container-blocks/descriptions.md +4 -45
- package/content/container-blocks/drawer.md +28 -92
- package/content/container-blocks/dropdownmenu.md +31 -119
- package/content/container-blocks/flex.md +62 -40
- package/content/container-blocks/footer.md +11 -15
- package/content/container-blocks/googlemapsheatmap.md +1 -1
- package/content/container-blocks/googlemapsscript.md +2 -2
- package/content/container-blocks/header.md +36 -71
- package/content/container-blocks/label.md +7 -34
- package/content/container-blocks/layout.md +34 -52
- package/content/container-blocks/masonry.md +71 -17
- package/content/container-blocks/mobilemenu.md +88 -131
- package/content/container-blocks/modal.md +26 -114
- package/content/container-blocks/pageheadermenu.md +31 -81
- package/content/container-blocks/pagesidebarlayout.md +26 -169
- package/content/container-blocks/pagesidermenu.md +42 -95
- package/content/container-blocks/popover.md +31 -34
- package/content/container-blocks/result.md +21 -32
- package/content/container-blocks/sider.md +122 -85
- package/content/container-blocks/span.md +7 -7
- package/content/container-blocks/spinner.md +3 -3
- package/content/container-blocks/splitter.md +68 -150
- package/content/container-blocks/tabs.md +12 -64
- package/content/container-blocks/tooltip.md +46 -22
- package/content/container-blocks/watermark.md +4 -22
- package/content/controls/reject.md +2 -4
- package/content/controls/throw.md +0 -1
- package/content/controls/try.md +2 -3
- package/content/deployment/deployment-environments.md +6 -8
- package/content/deployment/docker.md +7 -22
- package/content/deployment/node-server.md +0 -4
- package/content/deployment/vercel.md +6 -13
- package/content/display-blocks/aggrid.md +11 -26
- package/content/display-blocks/anchor.md +15 -15
- package/content/display-blocks/avatar.md +37 -57
- package/content/display-blocks/breadcrumb.md +21 -70
- package/content/display-blocks/button.md +18 -92
- package/content/display-blocks/calendar.md +0 -12
- package/content/display-blocks/dangeroushtml.md +1 -1
- package/content/display-blocks/difflist.md +1 -1
- package/content/display-blocks/difftimeline.md +1 -1
- package/content/display-blocks/divider.md +3 -64
- package/content/display-blocks/dropdownbutton.md +28 -79
- package/content/display-blocks/echart.md +1 -35
- package/content/display-blocks/floatbutton.md +42 -129
- package/content/display-blocks/html.md +1 -61
- package/content/display-blocks/icon.md +67 -250
- package/content/display-blocks/menu.md +73 -145
- package/content/display-blocks/message.md +124 -38
- package/content/display-blocks/notification.md +222 -94
- package/content/display-blocks/paragraph.md +9 -59
- package/content/display-blocks/progress.md +17 -96
- package/content/display-blocks/qrcode.md +4 -19
- package/content/display-blocks/s3download.md +1 -1
- package/content/display-blocks/search.md +7 -17
- package/content/display-blocks/skeletonbutton.md +0 -1
- package/content/display-blocks/skeletoninput.md +0 -1
- package/content/display-blocks/statistic.md +17 -75
- package/content/display-blocks/steps.md +25 -58
- package/content/display-blocks/tag.md +17 -102
- package/content/display-blocks/title.md +11 -50
- package/content/display-blocks/tour.md +23 -33
- package/content/input-blocks/autocomplete.md +9 -81
- package/content/input-blocks/buttonselector.md +8 -45
- package/content/input-blocks/checkboxselector.md +7 -97
- package/content/input-blocks/checkboxswitch.md +3 -33
- package/content/input-blocks/colorselector.md +14 -63
- package/content/input-blocks/daterangeselector.md +45 -165
- package/content/input-blocks/dateselector.md +41 -139
- package/content/input-blocks/datetimeselector.md +49 -158
- package/content/input-blocks/inputgallery.md +3 -3
- package/content/input-blocks/monthselector.md +52 -120
- package/content/input-blocks/multipleselector.md +39 -150
- package/content/input-blocks/numberinput.md +12 -98
- package/content/input-blocks/pagination.md +14 -70
- package/content/input-blocks/paragraphinput.md +37 -51
- package/content/input-blocks/passwordinput.md +6 -60
- package/content/input-blocks/phonenumberinput.md +26 -59
- package/content/input-blocks/radioselector.md +7 -83
- package/content/input-blocks/ratingslider.md +26 -57
- package/content/input-blocks/s3uploadbutton.md +24 -24
- package/content/input-blocks/s3uploaddragger.md +3 -3
- package/content/input-blocks/s3uploadphoto.md +5 -5
- package/content/input-blocks/segmentedselector.md +32 -72
- package/content/input-blocks/selector.md +21 -109
- package/content/input-blocks/switch.md +17 -65
- package/content/input-blocks/textarea.md +3 -32
- package/content/input-blocks/textinput.md +28 -69
- package/content/input-blocks/tiptapmentioninput.md +1 -1
- package/content/input-blocks/titleinput.md +12 -39
- package/content/input-blocks/treeinput.md +25 -49
- package/content/input-blocks/treemultipleselector.md +10 -127
- package/content/input-blocks/treeselector.md +9 -97
- package/content/input-blocks/upload.md +15 -64
- package/content/input-blocks/uploaddragger.md +1 -17
- package/content/input-blocks/uploadphoto.md +1 -18
- package/content/input-blocks/weekselector.md +55 -120
- package/content/list-blocks/controlledlist.md +15 -24
- package/content/list-blocks/list.md +11 -11
- package/content/list-blocks/masonrylist.md +34 -7
- package/content/migration/upgrade-guide.md +0 -1
- package/content/operators/_app.md +1 -2
- package/content/operators/_error.md +1 -23
- package/content/operators/_get.md +6 -6
- package/content/operators/_operator.md +3 -17
- package/content/operators/_ref.md +0 -258
- package/content/operators/_type.md +3 -14
- package/content/operators/_user.md +3 -3
- package/content/other/introduction.md +1 -1
- package/content/plugins/plugins-dev.md +0 -66
- package/content/plugins/plugins-introduction.md +1 -27
- package/content/testing/e2e-configuration.md +1 -8
- package/content/testing/e2e-getting-started.md +1 -1
- package/content/testing/e2e-introduction.md +1 -1
- package/content/tutorial/tutorial-actions-operators.md +2 -2
- package/content/tutorial/tutorial-add-blocks.md +2 -2
- package/content/tutorial/tutorial-create-page.md +2 -48
- package/content/tutorial/tutorial-display-data-page.md +3 -3
- package/content/tutorial/tutorial-requests-sql.md +0 -153
- package/content/tutorial/tutorial-start.md +1 -66
- package/content/user-authentication/login-and-logout.md +8 -158
- package/content/user-authentication/users-introduction.md +0 -8
- package/index.json +2 -6065
- package/package.json +1 -7
- package/scripts/extractAgentDocs.js +4 -36
- package/content/actions/emailotpsend.md +0 -58
- package/content/actions/emailotpverify.md +0 -77
- package/content/actions/getlocalstorage.md +0 -41
- package/content/actions/magiclinkverify.md +0 -113
- package/content/actions/posthogalias.md +0 -29
- package/content/actions/posthogcapture.md +0 -49
- package/content/actions/posthogcapturepageleave.md +0 -38
- package/content/actions/posthogfeatureflag.md +0 -50
- package/content/actions/posthoggroup.md +0 -37
- package/content/actions/posthogidentify.md +0 -54
- package/content/actions/posthoginit.md +0 -66
- package/content/actions/posthogoptin.md +0 -29
- package/content/actions/posthogoptout.md +0 -29
- package/content/actions/posthogpageview.md +0 -37
- package/content/actions/posthogreloadfeatureflags.md +0 -47
- package/content/actions/posthogreset.md +0 -28
- package/content/actions/posthogsetpersonproperties.md +0 -36
- package/content/actions/removelocalstorage.md +0 -26
- package/content/actions/setlocalstorage.md +0 -32
- package/content/adapter-reference/mongodbauthadapter.md +0 -46
- package/content/agents/agents-introduction.md +0 -107
- package/content/callback-reference/auth0logoutcallback.md +0 -54
- package/content/concepts/blocks.md +0 -150
- package/content/concepts/dynamic-page-content.md +0 -304
- package/content/concepts/html-attributes.md +0 -285
- package/content/concepts/lowdefy-api.md +0 -1110
- package/content/concepts/notifications.md +0 -155
- package/content/concepts/page-and-app-state.md +0 -244
- package/content/concepts/theming.md +0 -582
- package/content/connections/aigateway.md +0 -368
- package/content/connections/axioshttp.md +0 -181
- package/content/controls/while.md +0 -46
- package/content/display-blocks/clickablehtml.md +0 -239
- package/content/display-blocks/eventlog.md +0 -363
- package/content/display-blocks/pollingtimer.md +0 -263
- package/content/input-blocks/captcha.md +0 -40
- package/content/input-blocks/placesautocomplete.md +0 -319
- package/content/input-blocks/tagmultipleselector.md +0 -114
- package/content/input-blocks/tagselector.md +0 -114
- package/content/migration/auth-upgrade.md +0 -327
- package/content/migration/v3-to-v4.md +0 -417
- package/content/migration/v6-to-v7.md +0 -222
- package/content/operators/_cron.md +0 -292
- package/content/other/mongodbadapter.md +0 -3
- package/content/plugins/plugins-icon-sets.md +0 -267
- package/content/plugins/posthog.md +0 -275
- package/content/provider-reference/auth-js-providers.md +0 -107
- package/content/provider-reference/openidconnectprovider.md +0 -44
- package/content/testing/config-tests.md +0 -278
- package/content/tutorial/tutorial-requests-api.md +0 -155
- package/content/user-authentication/auth-configuration.md +0 -266
- package/content/user-authentication/auth-steps.md +0 -138
- package/content/user-authentication/mcp-oauth.md +0 -292
- package/content/user-authentication/organizations.md +0 -221
- package/content/user-authentication/protected-pages-apis.md +0 -97
- package/content/user-authentication/roles.md +0 -49
- package/content/user-authentication/two-factor.md +0 -229
- package/content/user-authentication/user-object.md +0 -58
- package/content/websockets/websocket-publish.md +0 -108
- package/content/websockets/websocket-subscriptions.md +0 -100
- package/content/websockets/websockets-introduction.md +0 -97
- package/hazards.json +0 -44
- package/scripts/hashDocSources.js +0 -38
- package/scripts/recordSourceHashes.js +0 -88
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Blocks
|
|
2
|
-
|
|
3
|
-
A Lowdefy page is compiled out of an arrangement of blocks. Every HTML element of this page is rendered as a result of a block placed and configured on the page. A Lowdefy Block is just a React component. A large number of default blocks make it easy to build apps with Lowdefy, and for more specific requirements, custom blocks can be added to apps as [plugins](/plugins).
|
|
4
|
-
|
|
5
|
-
Blocks make it simple for Lowdefy developers to create apps since they only have to decide which block type to use, where in the layout the block should render, and what the block should do by defining the block's `properties` and `events`. How a block implements these `properties` and `events` is up to the specific block type.
|
|
6
|
-
|
|
7
|
-
All Lowdefy blocks are categorized according to their primary function:
|
|
8
|
-
- `display` - Display page elements.
|
|
9
|
-
- `input` - Modify a value in [`state`](/page-and-app-state).
|
|
10
|
-
- `container` - Render other blocks into [`content slots`](/layout).
|
|
11
|
-
- `list` - Render `content slots` and blocks for each element in the data array represented in [`state`](/page-and-app-state).
|
|
12
|
-
|
|
13
|
-
When `state` updates or a requests call completes, the Lowdefy engine re-evaluates all operators and re-renders blocks for which the operator evaluation is different from the previous render result. The result is _live updates_ to all blocks on a page. Operators can be used to build _live update_ logic into all block fields, except for the `id`, `type`, `slots` and `blocks` fields.
|
|
14
|
-
|
|
15
|
-
The schema for a Lowdefy block is:
|
|
16
|
-
|
|
17
|
-
- `id: string`: An identifier for a block. For `Input` blocks the block `id` sets the field key which the block will modify in `state`. Field _dot-notation_ can be used to express fields which are nested in objects or arrays.
|
|
18
|
-
- `type: string`: __Required__ - This is the block type identifier and defines what block to use. The block type used must either be a default block type or can defined in the app's [`plugins`](/plugins) configuration.
|
|
19
|
-
- `properties: object`: All the settings passed to a block component. __Operators are evaluated__.
|
|
20
|
-
- `blocks: array`: An array of blocks to render to the default `content` slot for `container` and `list` blocks. See [layout](/layout-overview) for more details on how to use the `blocks` array.
|
|
21
|
-
- `slots: object`: Used to set the content slots and content layout settings for `container` and `list` blocks. See [layout](/layout-overview) for more details on how to use `slots`.
|
|
22
|
-
- `events: object`: Used to define [`actions`](/events-and-actions) that run when the block triggers an [`event`](/events-and-actions).
|
|
23
|
-
- `layout: object`: Used to define the [layout](/layout-overview) settings for a block. __Operators are evaluated__.
|
|
24
|
-
- `skeleton: object`: Used to overwrite a block's default loading behavior by rendering a skeleton. Any block types from [`@lowdefy/blocks-basic`](https://github.com/lowdefy/lowdefy/tree/main/packages/plugins/blocks/blocks-basic/src/blocks) and [`@lowdefy/blocks-loaders`](https://github.com/lowdefy/lowdefy/tree/main/packages/plugins/blocks/blocks-loaders/src/blocks) can be used in the skeleton definition, including sub-blocks. __Operators are evaluated__.
|
|
25
|
-
- `loading: boolean`: When true, renders the block in loading. If a `skeleton` is defined, it will be rendered, else the block's default loading behavior will be rendered. __Operators are evaluated__.
|
|
26
|
-
- `required: boolean | string`: For `input` blocks, whether or not a value is required in `state` when the [`Validate`](/Validate) action is called. Can be either a boolean or a string that is used as the validation error message . __Operators are evaluated__.
|
|
27
|
-
- `class: string | array | object`: CSS class names to apply to the block. Supports Tailwind CSS utility classes. __Operators are evaluated__.
|
|
28
|
-
- `style: css object`: Inline CSS styles. Flat CSS targets the layout wrapper. Use `.`-prefixed keys (e.g. `.element`, `.header`, `.body`) to target specific block parts. __Operators are evaluated__.
|
|
29
|
-
- `validate: array`: A list of validation tests to pass when the [`Validate`](/Validate) action is called. __Operators are evaluated__.
|
|
30
|
-
- `visible: boolean`: Controls whether or not to render a block. Operators are generally used here, and must evaluate to `false` to make the block invisible. Blocks with `visible: false` are excluded from `state`. __Operators are evaluated__.
|
|
31
|
-
|
|
32
|
-
## Block types
|
|
33
|
-
|
|
34
|
-
Lowdefy has a set of default block types as defined in the Lowdefy docs. Most of the default Lowdefy blocks aim to cover a very generic implementation of the [Ant Design](https://ant.design/components/overview) React component library, but some addition component libraries are also included. To use all the default block types, you can simply use the block `type` key, such as [`Button`](/Button), [`TextInput`](/TextInput), or [`Box`](/Box).
|
|
35
|
-
|
|
36
|
-
###### Default block type config example:
|
|
37
|
-
```yaml
|
|
38
|
-
lowdefy: 6.0.0
|
|
39
|
-
pages:
|
|
40
|
-
- id: example_dashboard
|
|
41
|
-
type: PageHeaderMenu
|
|
42
|
-
blocks:
|
|
43
|
-
- id: basic_chart
|
|
44
|
-
type: EChart
|
|
45
|
-
properties:
|
|
46
|
-
# ... EChart properties
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
However, the default types can be overwritten or additional types can be defined as required. This is done by defining additional [`plugins`](/plugins).
|
|
50
|
-
|
|
51
|
-
## Input block validation
|
|
52
|
-
|
|
53
|
-
All `input` block types maintain a value in the page [`state`](/page-and-app-state). This value is set to the field name matching the block `id`. Nested fields can be created by using _dot notation_ in the `id` to specify the field path, for example, `id: "a.b.c"` will produce a value `{"a": {"b": {"c": null }}}` in the page `state`.
|
|
54
|
-
|
|
55
|
-
Client side field validation can be applied by setting the `required` and / or `validate` block fields. Field validation is first evaluated when the [`Validate`](/Validate) action is invoked on a page.
|
|
56
|
-
|
|
57
|
-
##### required schema:
|
|
58
|
-
|
|
59
|
-
`required` can be a `boolean` or `string` type. When `required: true` the field label will indicate this with a red asterisk for user feedback, and a value will have to be supplied to the field in order to pass validation. If `required` is set to a `string`, this string will be used as the feedback message when the validation fails.
|
|
60
|
-
|
|
61
|
-
```yaml
|
|
62
|
-
- id: name
|
|
63
|
-
type: TextInput
|
|
64
|
-
required: Please provide your name.
|
|
65
|
-
properties:
|
|
66
|
-
title: Name
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
##### validate schema:
|
|
70
|
-
|
|
71
|
-
The `validate` field takes an `array` of test `objects` to evaluate before passing the field validation. This list of tests are evaluated sequentially, so the first test that fails will be used as the feedback message to the user.
|
|
72
|
-
|
|
73
|
-
The schema for the validation test `objects`:
|
|
74
|
-
- `pass: boolean`: __Required__ - The test that validates if this item passes or not. This is usually written with operators which evaluate to `true` or `false`. __Operators are evaluated__.
|
|
75
|
-
- `message: string`: __Required__ - The feedback message to the user if this validation tests fails. __Operators are evaluated__.
|
|
76
|
-
- `status: enum`: The feedback type to present to the user. Option are `error` and `warning`. Default is `error`. __Operators are evaluated__.
|
|
77
|
-
|
|
78
|
-
The following `validate` example first verifies that something was entered into the `email` field, then checks that the field passes a email regex validation using the [`_regex`](/_regex) operator:
|
|
79
|
-
|
|
80
|
-
```yaml
|
|
81
|
-
- id: email
|
|
82
|
-
type: TextInput
|
|
83
|
-
validate:
|
|
84
|
-
- message: Please enter a email address.
|
|
85
|
-
status: error
|
|
86
|
-
pass:
|
|
87
|
-
_ne:
|
|
88
|
-
- _state: email
|
|
89
|
-
- null
|
|
90
|
-
- message: Please provide a valid email address.
|
|
91
|
-
status: error
|
|
92
|
-
pass:
|
|
93
|
-
_regex: '^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'
|
|
94
|
-
properties:
|
|
95
|
-
title: Email
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Block events
|
|
99
|
-
|
|
100
|
-
By default all blocks implement `onMount` and `onMountAsync` events. Both the `onMount` and `onMountAsync` events are triggered when the block is mounted. For the `onMount` event, the block only mounts when the event action chain is completed, however, for the `onMountAsync` event, the block will mount as soon as possible while the event actions complete execution.
|
|
101
|
-
|
|
102
|
-
Apart from the `onMount` and `onMountAsync` events, most blocks also implement their own block specific events such as `onOpen` for [Modal](/Modal) or `onClick` for [Button](/Button). See the events tab on each block's documentation for more details.
|
|
103
|
-
|
|
104
|
-
See [the events and actions page](/events-and-actions) for more details.
|
|
105
|
-
|
|
106
|
-
## Block loading
|
|
107
|
-
|
|
108
|
-
Blocks will only start rendering when the `onInit` event has completed its actions. The `onMount` event on blocks will render blocks with loading active. By default, some blocks will change behavior while they are loading. For example, input blocks will be disabled during loading. The loading behavior of blocks can be controlled using the `loading` block property. Setting `loading` to `true` on a container or list block, will result in rendering all child blocks with loading active.
|
|
109
|
-
|
|
110
|
-
Often it is useful to render a skeleton of blocks instead of the blocks' default loading behavior. When block definitions are provided to the `skeleton` property on a block, this `skeleton` definition will be rendered when loading is active. Any block types from [`@lowdefy/blocks-basic`](https://github.com/lowdefy/lowdefy/tree/main/packages/plugins/blocks/blocks-basic/src/blocks) and [`@lowdefy/blocks-loaders`](https://github.com/lowdefy/lowdefy/tree/main/packages/plugins/blocks/blocks-loaders/src/blocks) can be used as the skeleton definition. Skeleton blocks are not real blocks, they do not have values in state or events, but they can contain other skeleton blocks as children.
|
|
111
|
-
|
|
112
|
-
##### Block `loading` and `skeleton` example:
|
|
113
|
-
|
|
114
|
-
```yaml
|
|
115
|
-
pages:
|
|
116
|
-
- id: page_one
|
|
117
|
-
type: Box
|
|
118
|
-
blocks:
|
|
119
|
-
# ...
|
|
120
|
-
- id: paragraph_one
|
|
121
|
-
type: Paragraph
|
|
122
|
-
loading:
|
|
123
|
-
_eq:
|
|
124
|
-
- _state: done
|
|
125
|
-
- false
|
|
126
|
-
skeleton:
|
|
127
|
-
type: SkeletonParagraph
|
|
128
|
-
properties:
|
|
129
|
-
lines: 1
|
|
130
|
-
properties:
|
|
131
|
-
content: Lorem ipsum dolor sit amet.
|
|
132
|
-
# ...
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## TLDR
|
|
136
|
-
- All user interfaces in Lowdefy are assembled out of blocks, which are React components.
|
|
137
|
-
#### Block types
|
|
138
|
-
- There are four block categories: `display`, `input`, `container` and `list`.
|
|
139
|
-
- Operators re-evaluate on every [`state`](/page-and-app-state) update or when request calls complete. This allows blocks to _live update_.
|
|
140
|
-
- Lowdefy has built in default block types, however this can be overwritten or extended with custom blocks by defining [`plugins`](/plugins) on the Lowdefy config root.
|
|
141
|
-
- `input` blocks maintain a value in `state` matching the block `id` key. _Dot notation_ applies to specify nested fields.
|
|
142
|
-
#### Block validation
|
|
143
|
-
- Field level input validation can be achieved by marking an `input` block as `required` or by specifying a list of `validate` tests.
|
|
144
|
-
- Validation is invoked using the [`Validate`](/Validate) action.
|
|
145
|
-
#### Block events
|
|
146
|
-
- All blocks have`onMount` and `onMountAsync` events.
|
|
147
|
-
- Each block implements it's own additional events such as `onClick` etc.
|
|
148
|
-
#### Block loading
|
|
149
|
-
- Some blocks gracefully handle a loading state while `onMount` events are being executed.
|
|
150
|
-
- A block's default loading can be overwritten by defining custom `skeleton` settings on a block.
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
# Dynamic Page Content
|
|
2
|
-
|
|
3
|
-
Dynamic page content lets parts of a page — or a whole page — be resolved on the server at page load. You place a `Dynamic` block on a page and point it at an [API endpoint](/lowdefy-api). When the page is requested, the server calls the endpoint's routine in-process, the routine returns block config, and the server validates that config and splices it into the page before responding. The client renders the result like any other page.
|
|
4
|
-
|
|
5
|
-
Dynamic page content is useful when the structure of a page depends on runtime data:
|
|
6
|
-
|
|
7
|
-
- A dashboard whose sections depend on the user's role, plan, or feature flags.
|
|
8
|
-
- A form generated from a workflow definition stored in a database.
|
|
9
|
-
- A product page whose layout comes from a CMS record.
|
|
10
|
-
- Table views, kanban columns, or navigation derived from tenant configuration.
|
|
11
|
-
|
|
12
|
-
For content where only the _data_ changes, use [requests](/connections-and-requests) — they are cached, re-fetchable, and don't reset page state. Reach for dynamic page content when the _blocks themselves_ need to change per request.
|
|
13
|
-
|
|
14
|
-
## How It Works
|
|
15
|
-
|
|
16
|
-
1. A user requests a page containing a `Dynamic` block.
|
|
17
|
-
2. The server checks page authorization as usual, then calls the endpoint configured on each `Dynamic` block — in-process, no HTTP. The endpoint's own auth is also checked against the user's session.
|
|
18
|
-
3. The routine executes with a payload containing the block's `params`, the `pageId`, the `blockId`, and the page request's `urlQuery`.
|
|
19
|
-
4. The routine returns `{ blocks: [...] }` — ordinary Lowdefy block config.
|
|
20
|
-
5. The server builds and validates the returned blocks: block, action and operator types must be in the app's client bundle, block properties are validated against each block type's schema, and `Request` actions must reference requests defined statically on the page.
|
|
21
|
-
6. The validated blocks become the `Dynamic` block's content, and the page is sent to the client.
|
|
22
|
-
|
|
23
|
-
If resolution fails, the `Dynamic` block renders its `fallback` slot (or nothing) and the error is logged on the server — the page still loads. Set `required: true` to fail the whole page request instead.
|
|
24
|
-
|
|
25
|
-
## The Dynamic Block
|
|
26
|
-
|
|
27
|
-
```yaml
|
|
28
|
-
- id: insights
|
|
29
|
-
type: Dynamic
|
|
30
|
-
properties:
|
|
31
|
-
endpointId: resolve_insights # Required. The endpoint that resolves this content.
|
|
32
|
-
params: # Optional. Static values passed to the endpoint.
|
|
33
|
-
area: insights
|
|
34
|
-
required: false # Optional. true fails the page load on resolution failure.
|
|
35
|
-
types: # Optional. Extra types the endpoint may return.
|
|
36
|
-
blocks:
|
|
37
|
-
- Statistic
|
|
38
|
-
slots:
|
|
39
|
-
fallback: # Optional. Rendered when resolution fails.
|
|
40
|
-
blocks:
|
|
41
|
-
- id: insights_unavailable
|
|
42
|
-
type: Html
|
|
43
|
-
properties:
|
|
44
|
-
html: Insights are unavailable right now.
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
`params` must not contain operators — the page config is static. Runtime values belong in the endpoint routine, which reads the payload with `_payload` and the session with `_user`.
|
|
48
|
-
|
|
49
|
-
A page that should be entirely dynamic is just a page whose body is a single `Dynamic` block.
|
|
50
|
-
|
|
51
|
-
## The Resolver Endpoint
|
|
52
|
-
|
|
53
|
-
The endpoint is a normal API endpoint. Use `type: InternalApi` so it has no HTTP route — it is then only callable by the server itself.
|
|
54
|
-
|
|
55
|
-
The endpoint receives this payload:
|
|
56
|
-
|
|
57
|
-
- `params: object` - The `Dynamic` block's `params`, verbatim.
|
|
58
|
-
- `pageId: string` - The page being resolved.
|
|
59
|
-
- `blockId: string` - The `Dynamic` block's id.
|
|
60
|
-
- `urlQuery: object` - The query parameters of the page request.
|
|
61
|
-
|
|
62
|
-
The routine must return an object with a `blocks` array:
|
|
63
|
-
|
|
64
|
-
```yaml
|
|
65
|
-
api:
|
|
66
|
-
- id: resolve_insights
|
|
67
|
-
type: InternalApi
|
|
68
|
-
auth:
|
|
69
|
-
public: false
|
|
70
|
-
routine:
|
|
71
|
-
- id: get_sections
|
|
72
|
-
type: MongoDBFind
|
|
73
|
-
connectionId: mongodb
|
|
74
|
-
properties:
|
|
75
|
-
query:
|
|
76
|
-
roles:
|
|
77
|
-
$in:
|
|
78
|
-
_user: roles
|
|
79
|
-
area:
|
|
80
|
-
_payload: params.area
|
|
81
|
-
- :return:
|
|
82
|
-
blocks:
|
|
83
|
-
_array.map:
|
|
84
|
-
on:
|
|
85
|
-
_step: get_sections
|
|
86
|
-
callback:
|
|
87
|
-
_function:
|
|
88
|
-
__object.assign:
|
|
89
|
-
- id:
|
|
90
|
-
__args: 0.section_id
|
|
91
|
-
type: Statistic
|
|
92
|
-
properties:
|
|
93
|
-
title:
|
|
94
|
-
__args: 0.title
|
|
95
|
-
value:
|
|
96
|
-
__args: 0.value
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
All operators registered on the server — including shared operators like `_state`, `_if` and `_string` — are evaluated during `:return` (`_state` reads the routine's own `:set_state` state). To defer an operator to the client instead, prefix it with one extra underscore, the same convention `_function` bodies use for `__args`:
|
|
100
|
-
|
|
101
|
-
```yaml
|
|
102
|
-
- :return:
|
|
103
|
-
blocks:
|
|
104
|
-
- id: greeting
|
|
105
|
-
type: Html
|
|
106
|
-
properties:
|
|
107
|
-
html:
|
|
108
|
-
__state: name_input # unescaped to `_state` — evaluates on the client
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
The server strips one leading underscore from `__`-prefixed keys in the returned config, so `__state` reaches the client as `_state` and binds to page state like any static block. Client-only operators (`_request`, `_global`, ...) have no server implementation and need no escaping, but the `__` form works for them too and is the consistent style.
|
|
112
|
-
|
|
113
|
-
> **Never place `_secret` in returned block config.** It evaluates during `:return` and the resulting value ships to the browser.
|
|
114
|
-
|
|
115
|
-
### Data in the returned config is literal
|
|
116
|
-
|
|
117
|
-
While a Dynamic block's endpoint evaluates its `:return`, data read at runtime stays data. That covers step results, the payload, routine state, `:for` items, and values built from data, such as `_json.parse` of a stored string. Three rules apply:
|
|
118
|
-
|
|
119
|
-
- **Data cannot carry operators.** No operator may return a value that contains operators. A record holding `{ _request: ... }` or `{ __state: ... }` fails resolution, and the block renders its fallback. The error names the operator and where in its result the operator was found.
|
|
120
|
-
- **Data cannot be a block or an action.** Without a [dynamic blocks policy](#dynamic-blocks-policies), every block and action in the returned content must be written in the `:return` config. Data may fill values inside those blocks (an id, a title, a type, a list of options), but a block or action that is itself data an operator returned, or a copy of it, fails resolution. To render stored block config, check it with a `ValidateDynamic` step under a policy.
|
|
121
|
-
- **Data cannot become an operator by merging.** An object from data that `_object.assign` merges with other config must not be left with an operator key alone, for example next to a key whose value is a function or evaluates to undefined on the client.
|
|
122
|
-
|
|
123
|
-
Data may nest at most 200 levels deep; deeper data fails resolution with an error saying so. Only keys that name one of the app's operators count as operators. Data such as a search hit's `{ _score: 0.5 }` or `{ _source: { ... } }` stays data.
|
|
124
|
-
|
|
125
|
-
Write client operators in the `:return` config itself. Only operators that pass their own already-checked params through — `_if`, `_switch`, `_if_none`, `_get`, `_args`, `_function`, `_log`, `_array` and `_object` (except `_object.fromEntries` and `_object.defineProperty`) — may return them, so mapping data rows into blocks with `_array.map` and `_function` works as shown above.
|
|
126
|
-
|
|
127
|
-
These patterns return config through data and fail resolution:
|
|
128
|
-
|
|
129
|
-
- **Blocks built up in routine state** (`:set_state` in a loop, then `_state` in `:return`). Build the list inside `:return` with `_array.map` or `_array.concat`.
|
|
130
|
-
- **Blocks returned by a nested endpoint** and read with `_step`. Share block config with `_ref` instead.
|
|
131
|
-
- **A `:for` over a literal list of block configs**, read with `_item`. Map the list inside `:return`.
|
|
132
|
-
- **Blocks built by `_js` or `_jsonata`.** Return the data from them and map it into blocks inside `:return`.
|
|
133
|
-
- **Stored block config returned as it is**, whether read directly, picked out with `_get`, or copied or extended by an `_array.map` callback (`__args: 0`, `__object.assign`). Put the Dynamic block under a policy and return the content through `ValidateDynamic` (see [Rendering stored content under a policy](#rendering-stored-content-under-a-policy)).
|
|
134
|
-
|
|
135
|
-
## Using urlQuery in the Routine
|
|
136
|
-
|
|
137
|
-
The page request's query string is forwarded to the resolver as `urlQuery` in the payload. A page loaded as `/products?category=shoes&sort=price` resolves with `urlQuery: { category: 'shoes', sort: 'price' }`:
|
|
138
|
-
|
|
139
|
-
```yaml
|
|
140
|
-
api:
|
|
141
|
-
- id: resolve_products
|
|
142
|
-
type: InternalApi
|
|
143
|
-
routine:
|
|
144
|
-
- id: get_products
|
|
145
|
-
type: MongoDBFind
|
|
146
|
-
connectionId: mongodb
|
|
147
|
-
properties:
|
|
148
|
-
query:
|
|
149
|
-
category:
|
|
150
|
-
_payload: urlQuery.category
|
|
151
|
-
options:
|
|
152
|
-
sort:
|
|
153
|
-
- - _payload: urlQuery.sort
|
|
154
|
-
- 1
|
|
155
|
-
- :return:
|
|
156
|
-
blocks:
|
|
157
|
-
- id: product_count
|
|
158
|
-
type: Html
|
|
159
|
-
properties:
|
|
160
|
-
html:
|
|
161
|
-
_string.concat:
|
|
162
|
-
- 'Found '
|
|
163
|
-
- _array.length:
|
|
164
|
-
_step: get_products
|
|
165
|
-
- ' products in "'
|
|
166
|
-
- _payload: urlQuery.category
|
|
167
|
-
- '"'
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
The resolver executes on every navigation to the page — a menu link, a `Link` action (including to the same page), or the browser back and forward buttons all re-resolve the content on the server. Dynamic page config is never served from a client cache.
|
|
171
|
-
|
|
172
|
-
## Client Bundle Types
|
|
173
|
-
|
|
174
|
-
The client bundle is fixed at build time — resolved content can only use block, action and operator types the build included. Types used on static pages are always available. To use a type that appears _only_ in dynamic content, declare it on the `Dynamic` block:
|
|
175
|
-
|
|
176
|
-
```yaml
|
|
177
|
-
- id: insights
|
|
178
|
-
type: Dynamic
|
|
179
|
-
properties:
|
|
180
|
-
endpointId: resolve_insights
|
|
181
|
-
types:
|
|
182
|
-
blocks:
|
|
183
|
-
- Statistic
|
|
184
|
-
- EChart
|
|
185
|
-
actions:
|
|
186
|
-
- CopyToClipboard
|
|
187
|
-
operators:
|
|
188
|
-
- _number
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
The build bundles declared types into the client. If a routine returns a type that is not in the bundle, resolution fails with a clear error instead of a silently broken page.
|
|
192
|
-
|
|
193
|
-
## Dynamic Blocks Policies
|
|
194
|
-
|
|
195
|
-
A dynamic blocks policy, declared under `policies.dynamicBlocks` in `lowdefy.yaml`, lets a Dynamic block render block config that is stored as data — a form built by a form builder, or config a model generated — within limits the app declares. The policy lists what the content may use. Anything unlisted fails, and the policy is checked twice with the same function: by a `ValidateDynamic` step before the content is stored, and on every page get before the content renders.
|
|
196
|
-
|
|
197
|
-
```yaml
|
|
198
|
-
policies:
|
|
199
|
-
dynamicBlocks:
|
|
200
|
-
- id: generated_form
|
|
201
|
-
blocks:
|
|
202
|
-
- Title
|
|
203
|
-
- Paragraph
|
|
204
|
-
- TextInput
|
|
205
|
-
- Selector
|
|
206
|
-
actions:
|
|
207
|
-
- SetState
|
|
208
|
-
- CallAPI
|
|
209
|
-
operators:
|
|
210
|
-
- _state
|
|
211
|
-
- _eq
|
|
212
|
-
- _if
|
|
213
|
-
endpoints:
|
|
214
|
-
- submit_form_response
|
|
215
|
-
links:
|
|
216
|
-
pages:
|
|
217
|
-
- form_submitted
|
|
218
|
-
origins:
|
|
219
|
-
- https://example.com
|
|
220
|
-
state: form
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
| Key | Default | Meaning |
|
|
224
|
-
| --------------- | --------- | ----------------------------------------------------------------------------------------------------------- |
|
|
225
|
-
| `id` | required | Policy id, referenced by Dynamic blocks and steps. |
|
|
226
|
-
| `blocks` | required | Block types the content may use. `Dynamic` cannot be listed. |
|
|
227
|
-
| `actions` | `[]` | Action types the content may use. |
|
|
228
|
-
| `operators` | `[]` | Client operator names. `_string` allows every `_string` method. `_operator` cannot be listed. |
|
|
229
|
-
| `endpoints` | `[]` | Endpoints a `CallAPI` action in the content may call. |
|
|
230
|
-
| `requests` | `[]` | Page requests a `Request` action may call. The build checks every page that hosts the policy defines them. |
|
|
231
|
-
| `links.pages` | `[]` | Pages the content may navigate to, by `pageId` or by an app path such as `/form_submitted`. |
|
|
232
|
-
| `links.origins` | `[]` | Exact origins (`https://example.com`) the content may link to or load from. |
|
|
233
|
-
| `state` | none | When set, input block ids, `SetState` keys and `_state` reads must sit under this state path. |
|
|
234
|
-
| `html` | `false` | When `false`, no string in the content may contain HTML tag syntax. |
|
|
235
|
-
| `limits` | see below | `depth` (10), `blocks` (500), `bytes` (262144) and `actionsPerEvent` (20). They can be raised, not removed. |
|
|
236
|
-
|
|
237
|
-
Besides the lists, a policy requires literal values wherever the content names a target: `properties`, `style`, events and action lists; the params of `Link`, `CallAPI`, `Request` and `SetState`; every `_state` key; and every `pageId` and URL. URLs must be app paths or use a listed origin — including image sources, markdown links and CSS `url()`. A block's properties schema marks which properties hold a URL (see [Plugins](/plugins-introduction)), so a URL property is checked whatever it is called, such as `Search` `indexUrl` or `QRCode` `icon`; every value no schema marks, including action params and the properties of blocks that mark none, is checked by its key (`url`, `href`, `src`, ...). URLs are read as the browser reads them, so surrounding spaces and embedded tabs or newlines do not hide one.
|
|
238
|
-
|
|
239
|
-
> With `html: false`, an operator that builds strings (`_string`, `_uri`, `_base64`, `_json`) can still assemble HTML at render time from content that passed the check. The build warns when a policy lists one. Leave them out of policies for generated content.
|
|
240
|
-
|
|
241
|
-
### Rendering stored content under a policy
|
|
242
|
-
|
|
243
|
-
Put the policy on the Dynamic block, and return the content through a `ValidateDynamic` step with the same policy. Data stays literal in a Dynamic endpoint (see [Data in the returned config is literal](#data-in-the-returned-config-is-literal)); the blocks of a `ValidateDynamic` step that passed with the block's policy are the one exception.
|
|
244
|
-
|
|
245
|
-
```yaml
|
|
246
|
-
pages:
|
|
247
|
-
- id: form
|
|
248
|
-
type: PageHeaderMenu
|
|
249
|
-
blocks:
|
|
250
|
-
- id: generated
|
|
251
|
-
type: Dynamic
|
|
252
|
-
properties:
|
|
253
|
-
endpointId: get_form_blocks
|
|
254
|
-
policy: generated_form
|
|
255
|
-
- id: submit
|
|
256
|
-
type: Button
|
|
257
|
-
properties:
|
|
258
|
-
title: Submit
|
|
259
|
-
events:
|
|
260
|
-
onClick:
|
|
261
|
-
- id: save
|
|
262
|
-
type: CallAPI
|
|
263
|
-
params:
|
|
264
|
-
endpointId: submit_form_response
|
|
265
|
-
payload:
|
|
266
|
-
answers:
|
|
267
|
-
_state: form
|
|
268
|
-
|
|
269
|
-
api:
|
|
270
|
-
- id: get_form_blocks
|
|
271
|
-
type: InternalApi
|
|
272
|
-
routine:
|
|
273
|
-
- id: get_form
|
|
274
|
-
type: MongoDBFindOne
|
|
275
|
-
connectionId: forms
|
|
276
|
-
properties:
|
|
277
|
-
query:
|
|
278
|
-
_id:
|
|
279
|
-
_payload: urlQuery.formId
|
|
280
|
-
- id: check
|
|
281
|
-
type: ValidateDynamic
|
|
282
|
-
properties:
|
|
283
|
-
policy: generated_form
|
|
284
|
-
blocks:
|
|
285
|
-
_step: get_form.blocks
|
|
286
|
-
- :return:
|
|
287
|
-
blocks:
|
|
288
|
-
_step: check.blocks
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
The policy's types are bundled into the page, so a Dynamic block with `policy` does not declare `properties.types`. Content that breaks the policy at page get renders the fallback, and the log lists each violation.
|
|
292
|
-
|
|
293
|
-
### Checking generated content
|
|
294
|
-
|
|
295
|
-
A generator calls `ValidateDynamic` with `throwOnInvalid: false` and feeds the errors back to the model. Each error has a `path` into the submitted content, a `rule` and a `message`. `DescribeDynamicPolicy` returns what the policy allows, with the properties schema of each block and the params schema of each action and operator, to build the prompt from. See [Validating Dynamic Content As A Routine Step](/lowdefy-api#validating-dynamic-content-as-a-routine-step).
|
|
296
|
-
|
|
297
|
-
## Rules and Limitations
|
|
298
|
-
|
|
299
|
-
- **No `requests` in resolved content.** Request artifacts are written at build time. Resolved blocks reference requests defined statically on the page via `Request` actions, or call endpoints with `CallAPI`.
|
|
300
|
-
- **Nesting is allowed** — resolved content may contain further `Dynamic` blocks, up to 5 levels deep.
|
|
301
|
-
- **Page state resets per visit.** Dynamic pages build a fresh context on every navigation, since the server may resolve different content each time. Keep cross-navigation state in `_global` or `_url_query`.
|
|
302
|
-
- **Endpoint auth always applies.** A public page pointing at a role-protected endpoint renders the fallback for users without the role — useful for role-gated sections.
|
|
303
|
-
- **Data cannot carry operators or be blocks.** Operators in step results, payload or state returned into `:return` fail resolution, and so do blocks and actions that are data, except the blocks of a `ValidateDynamic` step under the block's policy. See [Data in the returned config is literal](#data-in-the-returned-config-is-literal) and [Dynamic Blocks Policies](#dynamic-blocks-policies).
|
|
304
|
-
- **`blockId` namespace is shared.** Resolved blocks share the page's state namespace, so `_state` binds across static and dynamic blocks. Keep blockIds unique, as on any page.
|