@lowdefy/blocks-basic 0.0.0-experimental-20260909201552 → 0.0.0-experimental-20260911113654
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/Box/Box.static.js +36 -0
- package/dist/blocks/Box/meta.js +1 -0
- package/dist/blocks/DangerousHtml/DangerousHtml.js +5 -15
- package/dist/blocks/DangerousHtml/meta.js +1 -12
- package/dist/blocks/Html/Html.static.js +95 -0
- package/dist/blocks/Html/meta.js +1 -7
- package/dist/blocks/Icon/Icon.static.js +108 -0
- package/dist/blocks/Icon/meta.js +1 -0
- package/dist/blocks/Img/Img.static.js +39 -0
- package/dist/blocks/Img/meta.js +1 -0
- package/dist/blocks/Span/Span.static.js +35 -0
- package/dist/blocks/Span/meta.js +1 -0
- package/dist/static.js +26 -0
- package/package.json +12 -7
|
@@ -0,0 +1,36 @@
|
|
|
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 { htmlToText, isBlank } from '@lowdefy/block-utils/report';
|
|
16
|
+
/**
|
|
17
|
+
* Box → a `stack` of its `content` area. The Box block shows
|
|
18
|
+
* `properties.content` when set instead of its child area, so the report
|
|
19
|
+
* mirrors that: a content string becomes a single `text` (markup flattened),
|
|
20
|
+
* otherwise the walked area passes through as a stack. An empty, content-less
|
|
21
|
+
* box yields no node.
|
|
22
|
+
*/ export const Box = {
|
|
23
|
+
toReport: ({ block, areas = {} })=>{
|
|
24
|
+
const content = block.properties?.content;
|
|
25
|
+
if (!isBlank(content)) return {
|
|
26
|
+
kind: 'text',
|
|
27
|
+
text: htmlToText(content)
|
|
28
|
+
};
|
|
29
|
+
const nodes = areas.content ?? [];
|
|
30
|
+
if (nodes.length === 0) return null;
|
|
31
|
+
return {
|
|
32
|
+
kind: 'stack',
|
|
33
|
+
children: nodes
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
package/dist/blocks/Box/meta.js
CHANGED
|
@@ -17,20 +17,13 @@ import DOMPurify from 'dompurify';
|
|
|
17
17
|
import { withBlockDefaults } from '@lowdefy/block-utils';
|
|
18
18
|
import { type } from '@lowdefy/helpers';
|
|
19
19
|
let DangerousHtml = class DangerousHtml extends React.Component {
|
|
20
|
-
sanitize() {
|
|
21
|
-
const { html, DOMPurifyOptions } = this.props.properties;
|
|
22
|
-
if (!this.warnedOptionsChange && JSON.stringify(DOMPurifyOptions) !== JSON.stringify(this.DOMPurifyOptions)) {
|
|
23
|
-
this.warnedOptionsChange = true;
|
|
24
|
-
console.warn(`DangerousHtml block "${this.props.blockId}": DOMPurifyOptions changed after mount and the change is ignored. The sanitizer options are fixed at the first render; write them as a literal, not from state.`);
|
|
25
|
-
}
|
|
26
|
-
const htmlString = type.isNone(html) ? '' : html.toString();
|
|
27
|
-
this.div.innerHTML = DOMPurify.sanitize(htmlString, this.DOMPurifyOptions);
|
|
28
|
-
}
|
|
29
20
|
componentDidMount() {
|
|
30
|
-
this.
|
|
21
|
+
const htmlString = type.isNone(this.props.properties.html) ? '' : this.props.properties.html.toString();
|
|
22
|
+
this.div.innerHTML = DOMPurify.sanitize(htmlString, this.DOMPurifyOptions);
|
|
31
23
|
}
|
|
32
24
|
componentDidUpdate() {
|
|
33
|
-
this.
|
|
25
|
+
const htmlString = type.isNone(this.props.properties.html) ? '' : this.props.properties.html.toString();
|
|
26
|
+
this.div.innerHTML = DOMPurify.sanitize(htmlString, this.DOMPurifyOptions);
|
|
34
27
|
}
|
|
35
28
|
render() {
|
|
36
29
|
const { blockId, classNames, styles } = this.props;
|
|
@@ -51,11 +44,8 @@ let DangerousHtml = class DangerousHtml extends React.Component {
|
|
|
51
44
|
this.div = {
|
|
52
45
|
innerHTML: ''
|
|
53
46
|
};
|
|
54
|
-
//
|
|
55
|
-
// value could loosen the sanitizer from state. A change is refused loudly
|
|
56
|
-
// rather than ignored silently.
|
|
47
|
+
// we do not revaluate DOMPurifyOptions improve options safety by not making options dynamic.
|
|
57
48
|
this.DOMPurifyOptions = props.properties.DOMPurifyOptions;
|
|
58
|
-
this.warnedOptionsChange = false;
|
|
59
49
|
}
|
|
60
50
|
};
|
|
61
51
|
export default withBlockDefaults(DangerousHtml);
|
|
@@ -14,23 +14,12 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ export default {
|
|
16
16
|
category: 'display',
|
|
17
|
+
static: true,
|
|
17
18
|
icons: [],
|
|
18
19
|
valueType: null,
|
|
19
20
|
cssKeys: {
|
|
20
21
|
element: 'The DangerousHtml element.'
|
|
21
22
|
},
|
|
22
|
-
hazards: [
|
|
23
|
-
{
|
|
24
|
-
id: 'dangerous-html-unsanitised',
|
|
25
|
-
message: 'Content is rendered without sanitising, so never pass caller-supplied or database HTML through it. Use Html for anything a user could have written.',
|
|
26
|
-
see: 'display-blocks/dangeroushtml'
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: 'dompurify-options-first-render',
|
|
30
|
-
message: 'DOMPurifyOptions is read once when the block mounts, so a value that changes after the first render has no effect. Set it statically or remount the block.',
|
|
31
|
-
see: 'display-blocks/dangeroushtml'
|
|
32
|
-
}
|
|
33
|
-
],
|
|
34
23
|
properties: {
|
|
35
24
|
type: 'object',
|
|
36
25
|
additionalProperties: false,
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
* Html / DangerousHtml → `svg`.
|
|
17
|
+
*
|
|
18
|
+
* Reports use Html blocks for custom components (KPI tiles, badges, styled
|
|
19
|
+
* headings), so the report renders the markup rather than skipping it. The
|
|
20
|
+
* layout engine belongs to the reports plugin, which hands it in as
|
|
21
|
+
* `context.renderHtml`: this renderer only decides the box (the column width
|
|
22
|
+
* and the block's own style height) and reports what the engine cannot draw.
|
|
23
|
+
* Keeping the engine out of this package means an app that never renders a
|
|
24
|
+
* report installs no native layout binary for its Html blocks.
|
|
25
|
+
*
|
|
26
|
+
* `DangerousHtml` shares this renderer: sanitization is a client concern, so
|
|
27
|
+
* the report renders the same string the page would.
|
|
28
|
+
*/ import { type } from '@lowdefy/helpers';
|
|
29
|
+
import { isBlank, styleValue, toPoints } from '@lowdefy/block-utils/report';
|
|
30
|
+
// The html engine has no table layout algorithm, so `<table>` cells run on
|
|
31
|
+
// inline. The block renders, mislaid out, with this warning naming the fix.
|
|
32
|
+
const TABLE_PATTERN = /<table[\s/>]/i;
|
|
33
|
+
// The html engine never fetches an image, so an `<img>` draws nothing and takes
|
|
34
|
+
// no space. Warn rather than let a logo vanish silently.
|
|
35
|
+
const IMG_PATTERN = /<img[\s/>]/i;
|
|
36
|
+
let missingRendererWarned = false;
|
|
37
|
+
function warnMissingRenderer({ block, context }) {
|
|
38
|
+
if (missingRendererWarned) return;
|
|
39
|
+
missingRendererWarned = true;
|
|
40
|
+
context?.logger?.warn?.({
|
|
41
|
+
blockId: block.blockId
|
|
42
|
+
}, `${block.type} block '${block.blockId}' skipped: the reports plugin supplied no html renderer (context.renderHtml). Html blocks render only through @lowdefy/plugin-reports.`);
|
|
43
|
+
}
|
|
44
|
+
const toReport = async ({ block, layout, context })=>{
|
|
45
|
+
const html = block.properties?.html;
|
|
46
|
+
if (isBlank(html)) return null;
|
|
47
|
+
const source = String(html);
|
|
48
|
+
if (!type.isFunction(context?.renderHtml)) {
|
|
49
|
+
warnMissingRenderer({
|
|
50
|
+
block,
|
|
51
|
+
context
|
|
52
|
+
});
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (TABLE_PATTERN.test(source)) {
|
|
56
|
+
context.logger?.warn?.({
|
|
57
|
+
blockId: block.blockId
|
|
58
|
+
}, `${block.type} block '${block.blockId}' contains <table> markup, which reports cannot lay out: cells run on inline. Use flex markup, or a table block, for tabular report content.`);
|
|
59
|
+
}
|
|
60
|
+
if (IMG_PATTERN.test(source)) {
|
|
61
|
+
context.logger?.warn?.({
|
|
62
|
+
blockId: block.blockId
|
|
63
|
+
}, `${block.type} block '${block.blockId}' contains <img> markup, which reports cannot load: the image draws nothing and takes no space. Use an Img block for report images.`);
|
|
64
|
+
}
|
|
65
|
+
const width = layout.width;
|
|
66
|
+
// The block's own style height wins over auto-measurement, so tiles in a row
|
|
67
|
+
// line up even when one label wraps.
|
|
68
|
+
const styleHeight = toPoints(styleValue(block.style, 'height'));
|
|
69
|
+
try {
|
|
70
|
+
const { svg, height } = await context.renderHtml({
|
|
71
|
+
html: source,
|
|
72
|
+
width,
|
|
73
|
+
height: styleHeight
|
|
74
|
+
});
|
|
75
|
+
// Markup that measures to nothing (an empty div, a comment) has nothing to
|
|
76
|
+
// draw, and a zero-height node would still take its margin in the PDF.
|
|
77
|
+
if (height === 0) return null;
|
|
78
|
+
return {
|
|
79
|
+
kind: 'svg',
|
|
80
|
+
svg,
|
|
81
|
+
width,
|
|
82
|
+
height
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
context.logger?.warn?.({
|
|
86
|
+
blockId: block.blockId,
|
|
87
|
+
err: error
|
|
88
|
+
}, `${block.type} block '${block.blockId}' failed to render and was skipped: ${error.message}`);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
export const Html = {
|
|
93
|
+
toReport
|
|
94
|
+
};
|
|
95
|
+
/** DangerousHtml renders through the same engine, from the same property. */ export const DangerousHtml = Html;
|
package/dist/blocks/Html/meta.js
CHANGED
|
@@ -14,18 +14,12 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ export default {
|
|
16
16
|
category: 'display',
|
|
17
|
+
static: true,
|
|
17
18
|
icons: [],
|
|
18
19
|
valueType: null,
|
|
19
20
|
cssKeys: {
|
|
20
21
|
element: 'The Html element.'
|
|
21
22
|
},
|
|
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. Use DangerousHtml for a trusted <style> block, or style the block through its style and class properties.',
|
|
26
|
-
see: 'display-blocks/html'
|
|
27
|
-
}
|
|
28
|
-
],
|
|
29
23
|
events: {
|
|
30
24
|
onTextSelection: 'Trigger action when text is selected and pass selected text to the event object.'
|
|
31
25
|
},
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
* Icon → `svg`. Production KPI tiles pair a number with a movement arrow, so an
|
|
17
|
+
* unrendered Icon costs the report the direction of every trend it shows.
|
|
18
|
+
*
|
|
19
|
+
* The icon components are react-icons functions, taken from `context.icons`:
|
|
20
|
+
* the build's tree-shaken icon artifact, the same map the client resolves names
|
|
21
|
+
* against, so a report can only draw icons the app actually bundles.
|
|
22
|
+
*
|
|
23
|
+
* React renders them: `react` and `react-dom/server` are imported lazily on
|
|
24
|
+
* first use, which keeps the static entry free of React at module load (every
|
|
25
|
+
* report imports this package for Box and Span, but only a page with an Icon
|
|
26
|
+
* pays for React). Serialising the element tree by hand was rejected because
|
|
27
|
+
* react-icons ships React-cased attributes (`fillRule`, `strokeLinecap`,
|
|
28
|
+
* `clipPath`; 457 of them in one icon family alone), so a second serialiser
|
|
29
|
+
* would have to carry its own copy of React's SVG attribute-casing table.
|
|
30
|
+
*/ import { type } from '@lowdefy/helpers';
|
|
31
|
+
import { styleValue, toPoints } from '@lowdefy/block-utils/report';
|
|
32
|
+
// A line of body text is ~14pt, which is the size an icon beside a label wants.
|
|
33
|
+
const DEFAULT_SIZE = 14;
|
|
34
|
+
// react-icons paints with `currentColor`; the client inherits the surrounding
|
|
35
|
+
// text colour, and a document has none, so fall back to the body text colour.
|
|
36
|
+
const DEFAULT_COLOR = '#262626';
|
|
37
|
+
let renderer;
|
|
38
|
+
// react-dom/server resolves the React-cased attributes and the `currentColor`
|
|
39
|
+
// default the same way the browser does. Loaded once per process.
|
|
40
|
+
async function getRenderer() {
|
|
41
|
+
if (!renderer) {
|
|
42
|
+
const [{ createElement }, reactDomServer] = await Promise.all([
|
|
43
|
+
import('react'),
|
|
44
|
+
import('react-dom/server')
|
|
45
|
+
]);
|
|
46
|
+
const { renderToStaticMarkup } = reactDomServer.default ?? reactDomServer;
|
|
47
|
+
renderer = {
|
|
48
|
+
createElement,
|
|
49
|
+
renderToStaticMarkup
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return renderer;
|
|
53
|
+
}
|
|
54
|
+
const toReport = async ({ block, context })=>{
|
|
55
|
+
// `properties` may be the icon name alone (`type: Icon, properties: AiFillHome`),
|
|
56
|
+
// exactly as the client's createIcon reads it.
|
|
57
|
+
const properties = type.isString(block.properties) ? {
|
|
58
|
+
name: block.properties
|
|
59
|
+
} : block.properties ?? {};
|
|
60
|
+
const name = properties.name;
|
|
61
|
+
const Component = name ? context?.icons?.[name] : undefined;
|
|
62
|
+
if (!Component) {
|
|
63
|
+
// The page shows a fallback glyph for an unknown name; a document is better
|
|
64
|
+
// off without it, so skip and name the block instead.
|
|
65
|
+
context?.logger?.warn?.({
|
|
66
|
+
blockId: block.blockId,
|
|
67
|
+
name
|
|
68
|
+
}, `Icon block '${block.blockId}' names icon '${name}', which the app does not bundle. Skipped.`);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const size = toPoints(properties.size) ?? toPoints(styleValue(block.style, 'fontSize')) ?? DEFAULT_SIZE;
|
|
72
|
+
const rawColor = properties.color ?? styleValue(block.style, 'color') ?? DEFAULT_COLOR;
|
|
73
|
+
// The colour is spliced into SVG markup below, so reject any value carrying
|
|
74
|
+
// markup characters (a config-authored `red"/><image .../>`) and fall back to
|
|
75
|
+
// the default rather than let it break out of the attribute.
|
|
76
|
+
const color = /[<>"]/.test(String(rawColor)) ? DEFAULT_COLOR : rawColor;
|
|
77
|
+
if (!type.isUndefined(properties.rotate) || properties.spin === true) {
|
|
78
|
+
context?.logger?.debug?.({
|
|
79
|
+
blockId: block.blockId
|
|
80
|
+
}, `Report icons ignore rotate and spin on block '${block.blockId}'.`);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const { createElement, renderToStaticMarkup } = await getRenderer();
|
|
84
|
+
const markup = renderToStaticMarkup(createElement(Component, {
|
|
85
|
+
size,
|
|
86
|
+
color,
|
|
87
|
+
title: properties.title
|
|
88
|
+
}));
|
|
89
|
+
// pdfmake's SVG renderer has no CSS cascade, so it cannot resolve the
|
|
90
|
+
// keyword react-icons paints with; bake the resolved colour in.
|
|
91
|
+
const svg = markup.split('currentColor').join(color);
|
|
92
|
+
return {
|
|
93
|
+
kind: 'svg',
|
|
94
|
+
svg,
|
|
95
|
+
width: size,
|
|
96
|
+
height: size
|
|
97
|
+
};
|
|
98
|
+
} catch (error) {
|
|
99
|
+
context?.logger?.warn?.({
|
|
100
|
+
blockId: block.blockId,
|
|
101
|
+
err: error
|
|
102
|
+
}, `Icon block '${block.blockId}' failed to render and was skipped: ${error.message}`);
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
export const Icon = {
|
|
107
|
+
toReport
|
|
108
|
+
};
|
package/dist/blocks/Icon/meta.js
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
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 { type } from '@lowdefy/helpers';
|
|
16
|
+
import { isBlank } from '@lowdefy/block-utils/report';
|
|
17
|
+
/**
|
|
18
|
+
* Img → `image`. `properties.src` names the source; the reports plugin resolves
|
|
19
|
+
* the bytes centrally (data URI, public asset, or guarded fetch). The block's
|
|
20
|
+
* `width`/`height` properties become the IR node's width/height, carried
|
|
21
|
+
* through as PostScript points, the same unit every other sizeable IR node
|
|
22
|
+
* uses. With neither set the image keeps its natural size, capped to the
|
|
23
|
+
* content width. An empty src yields no node.
|
|
24
|
+
*/ export const Img = {
|
|
25
|
+
toReport: ({ block })=>{
|
|
26
|
+
const { src, width, height } = block.properties ?? {};
|
|
27
|
+
if (isBlank(src)) return null;
|
|
28
|
+
return {
|
|
29
|
+
kind: 'image',
|
|
30
|
+
src: String(src),
|
|
31
|
+
...type.isNumber(width) ? {
|
|
32
|
+
width
|
|
33
|
+
} : {},
|
|
34
|
+
...type.isNumber(height) ? {
|
|
35
|
+
height
|
|
36
|
+
} : {}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
};
|
package/dist/blocks/Img/meta.js
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
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 { htmlToText, isBlank } from '@lowdefy/block-utils/report';
|
|
16
|
+
/**
|
|
17
|
+
* Span → `text`. The Span block shows `properties.content` when set, otherwise
|
|
18
|
+
* its `content` area; the report mirrors that precedence. A content string
|
|
19
|
+
* becomes a `text` (markup flattened); otherwise the walked area is wrapped in
|
|
20
|
+
* a stack. A Span with neither yields no node.
|
|
21
|
+
*/ export const Span = {
|
|
22
|
+
toReport: ({ block, areas = {} })=>{
|
|
23
|
+
const content = block.properties?.content;
|
|
24
|
+
if (!isBlank(content)) return {
|
|
25
|
+
kind: 'text',
|
|
26
|
+
text: htmlToText(content)
|
|
27
|
+
};
|
|
28
|
+
const nodes = areas.content ?? [];
|
|
29
|
+
if (nodes.length === 0) return null;
|
|
30
|
+
return {
|
|
31
|
+
kind: 'stack',
|
|
32
|
+
children: nodes
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
};
|
package/dist/blocks/Span/meta.js
CHANGED
package/dist/static.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
* Static report renderers for blocks-basic. Each export is keyed by block type
|
|
17
|
+
* and exposes `{ toReport }`, mapping a block's evaluated properties (and, for
|
|
18
|
+
* containers, its walked `areas`) to report IR. This entry must stay free of
|
|
19
|
+
* React at load time so the server can load the registry without a browser
|
|
20
|
+
* runtime; renderers emit plain IR object literals, never pdfmake or ExcelJS
|
|
21
|
+
* objects. (The Icon renderer imports React lazily, on the first icon it draws.)
|
|
22
|
+
*/ export { Img } from './blocks/Img/Img.static.js';
|
|
23
|
+
export { Span } from './blocks/Span/Span.static.js';
|
|
24
|
+
export { Box } from './blocks/Box/Box.static.js';
|
|
25
|
+
export { Html, DangerousHtml } from './blocks/Html/Html.static.js';
|
|
26
|
+
export { Icon } from './blocks/Icon/Icon.static.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/blocks-basic",
|
|
3
|
-
"version": "0.0.0-experimental-
|
|
3
|
+
"version": "0.0.0-experimental-20260911113654",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Basic html Lowdefy blocks.",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -36,14 +36,15 @@
|
|
|
36
36
|
"./blocks": "./dist/blocks.js",
|
|
37
37
|
"./e2e": "./dist/e2e.js",
|
|
38
38
|
"./metas": "./dist/metas.js",
|
|
39
|
+
"./static": "./dist/static.js",
|
|
39
40
|
"./types": "./dist/types.js"
|
|
40
41
|
},
|
|
41
42
|
"files": [
|
|
42
43
|
"dist/*"
|
|
43
44
|
],
|
|
44
45
|
"dependencies": {
|
|
45
|
-
"@lowdefy/block-utils": "0.0.0-experimental-
|
|
46
|
-
"@lowdefy/helpers": "0.0.0-experimental-
|
|
46
|
+
"@lowdefy/block-utils": "0.0.0-experimental-20260911113654",
|
|
47
|
+
"@lowdefy/helpers": "0.0.0-experimental-20260911113654",
|
|
47
48
|
"dompurify": "3.4.2"
|
|
48
49
|
},
|
|
49
50
|
"peerDependencies": {
|
|
@@ -51,12 +52,15 @@
|
|
|
51
52
|
"react-dom": ">=18"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
|
-
"@
|
|
55
|
-
"@lowdefy/e2e
|
|
55
|
+
"@jest/globals": "28.1.3",
|
|
56
|
+
"@lowdefy/block-dev-e2e": "0.0.0-experimental-20260911113654",
|
|
57
|
+
"@lowdefy/e2e-utils": "0.0.0-experimental-20260911113654",
|
|
56
58
|
"@playwright/test": "1.59.1",
|
|
57
59
|
"@swc/cli": "0.8.1",
|
|
58
60
|
"@swc/core": "1.15.32",
|
|
59
|
-
"
|
|
61
|
+
"@swc/jest": "0.2.39",
|
|
62
|
+
"copyfiles": "2.4.1",
|
|
63
|
+
"jest": "28.1.3"
|
|
60
64
|
},
|
|
61
65
|
"publishConfig": {
|
|
62
66
|
"access": "public"
|
|
@@ -66,6 +70,7 @@
|
|
|
66
70
|
"clean": "rm -rf dist",
|
|
67
71
|
"copyfiles": "copyfiles -u 1 \"./src/**/*\" dist -e \"./src/**/*.js\" -e \"./src/**/*.snap\"",
|
|
68
72
|
"e2e": "playwright test --config e2e/playwright.config.js",
|
|
69
|
-
"e2e:ui": "playwright test --config e2e/playwright.config.js --ui"
|
|
73
|
+
"e2e:ui": "playwright test --config e2e/playwright.config.js --ui",
|
|
74
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
|
70
75
|
}
|
|
71
76
|
}
|