@valbuild/next 0.57.0 → 0.58.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
CHANGED
|
@@ -73,7 +73,7 @@ As a CMS, Val is useful because:
|
|
|
73
73
|
- **richtext** support is built-in
|
|
74
74
|
- built-in **visual editing** which lets editors click-then-edit content (and therefore **code**!) directly in your app
|
|
75
75
|
|
|
76
|
-

|
|
77
77
|
|
|
78
78
|
<details>
|
|
79
79
|
<summary>Definition: editor</summary>
|
|
@@ -86,11 +86,11 @@ But, with the benefits of **hard-coded** content:
|
|
|
86
86
|
|
|
87
87
|
- works seamlessly **locally** or with git **branches**
|
|
88
88
|
- content is **type-checked** so you can spend less time on figuring out why something isn't working
|
|
89
|
-

|
|
90
90
|
- content can be refactored (change names, etc) just as if it was hard-coded (because it is)
|
|
91
|
-

|
|
92
92
|
- works as normal with your **favorite IDE** without any plugins: search for content, references to usages, ...
|
|
93
|
-

|
|
94
94
|
- **no** need for **code-gen** and extra build steps
|
|
95
95
|
- **fast** since the content is literally hosted with the application
|
|
96
96
|
- content is **always there** and can never fail (since it is not loaded from somewhere)
|
|
@@ -137,7 +137,7 @@ In addition, if you have a "content model", i.e. content schemas, that rarely ch
|
|
|
137
137
|
|
|
138
138
|
If that is the case, we recommend having a look at [sanity](https://sanity.io) instead (we have no affiliation, but if we didn't have Val we would use Sanity).
|
|
139
139
|
|
|
140
|
-
**NOTE**: Our experience is that however nice it sounds, it is hard to "nail" the content model down. Usually content is derived from what you want to present, not vice-versa. In addition, you should think carefully whether you _really_ want to present the exact same content on all these different surfaces.
|
|
140
|
+
**NOTE**: Our experience is that, however nice it sounds, it is hard to "nail" the content model down. Usually content is derived from what you want to present, not vice-versa. In addition, you should think carefully whether you _really_ want to present the exact same content on all these different surfaces.
|
|
141
141
|
|
|
142
142
|
## Examples
|
|
143
143
|
|
|
@@ -155,7 +155,7 @@ npm install @valbuild/next@latest @valbuild/eslint-plugin@latest
|
|
|
155
155
|
- Run the init script:
|
|
156
156
|
|
|
157
157
|
```sh
|
|
158
|
-
npx @valbuild/init
|
|
158
|
+
npx @valbuild/init@latest
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
### Add editor support
|
|
@@ -398,6 +398,133 @@ export default function Page() {
|
|
|
398
398
|
}
|
|
399
399
|
```
|
|
400
400
|
|
|
401
|
+
#### ValRichText: theme property
|
|
402
|
+
|
|
403
|
+
To add classes to `ValRichText` you can use the theme property:
|
|
404
|
+
|
|
405
|
+
```tsx
|
|
406
|
+
<ValRichText
|
|
407
|
+
theme={{
|
|
408
|
+
p: "font-sans",
|
|
409
|
+
// etc
|
|
410
|
+
}}
|
|
411
|
+
>
|
|
412
|
+
{content}
|
|
413
|
+
</ValRichText>
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**NOTE**: if a theme is defined, you must define a mapping for every tag that the you get. What tags you have is decided based on the `options` defined on the `s.richtext()` schema. For example: `s.richtext({ headings: ["h1"]; bold: true; img: true})` forces you to map the class for at least: `h1`, `bold` and `img`:
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
<ValRichText
|
|
420
|
+
theme={{
|
|
421
|
+
h1: "text-4xl font-bold",
|
|
422
|
+
bold: "font-bold",
|
|
423
|
+
img: null, // either a string or null is required
|
|
424
|
+
}}
|
|
425
|
+
>
|
|
426
|
+
{content satisfies RichText<{ headings: ["h1"]; bold: true; img: true }>}
|
|
427
|
+
</ValRichText>
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**NOTE**: the reason you must define themes for every tag that the RichText is that this will force you to revisit the themes that are used if the schema changes. The alternative would be to accept changes to the schema.
|
|
431
|
+
|
|
432
|
+
### ValRichText: transform property
|
|
433
|
+
|
|
434
|
+
Vals `RichText` type maps RichText 1-to-1 with semantic HTML5.
|
|
435
|
+
|
|
436
|
+
If you want to customize the type of elements which are rendered, you can use the `transform` property.
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
<ValRichText
|
|
440
|
+
transform={(node, _children, className) => {
|
|
441
|
+
if (typeof node !== "string" && node.tag === "img") {
|
|
442
|
+
return (
|
|
443
|
+
<div className="my-wrapper-class">
|
|
444
|
+
<img {...node} className={className} />
|
|
445
|
+
</div>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
// if transform returns undefined the default render will be used
|
|
449
|
+
}}
|
|
450
|
+
>
|
|
451
|
+
{content}
|
|
452
|
+
</ValRichText>
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### The RichText type
|
|
456
|
+
|
|
457
|
+
The `RichText` type is actually an AST (abstract syntax tree) representing semantic HTML5 elements.
|
|
458
|
+
|
|
459
|
+
That means they look something like this:
|
|
460
|
+
|
|
461
|
+
```ts
|
|
462
|
+
type RichTextNode = {
|
|
463
|
+
tag:
|
|
464
|
+
| "img"
|
|
465
|
+
| "a"
|
|
466
|
+
| "ul"
|
|
467
|
+
| "ol"
|
|
468
|
+
| "h1"
|
|
469
|
+
| "h2"
|
|
470
|
+
| "h3"
|
|
471
|
+
| "h4"
|
|
472
|
+
| "h5"
|
|
473
|
+
| "h6"
|
|
474
|
+
| "br"
|
|
475
|
+
| "p"
|
|
476
|
+
| "li"
|
|
477
|
+
| "span";
|
|
478
|
+
classes: "bold" | "line-through" | "italic"; // all styling classes
|
|
479
|
+
children: RichTextNode[] | undefined;
|
|
480
|
+
};
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### RichText: full custom
|
|
484
|
+
|
|
485
|
+
The `RichText` type maps 1-to-1 to HTML.
|
|
486
|
+
That means it is straightforward to build your own implementation of a React component that renders `RichText`.
|
|
487
|
+
|
|
488
|
+
This example is a simplified version of the `ValRichText` component.
|
|
489
|
+
You can use this as a template to create your own.
|
|
490
|
+
|
|
491
|
+
NOTE: before writing your own, make sure you check out the `theme` and `transform` properties on the `ValRichText` - most simpler cases should be covered by them.
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
export function ValRichText({
|
|
495
|
+
children: root,
|
|
496
|
+
}: {
|
|
497
|
+
children: RichText<MyRichTextOptions>;
|
|
498
|
+
}) {
|
|
499
|
+
function build(
|
|
500
|
+
node: RichTextNode<MyRichTextOptions>,
|
|
501
|
+
key?: number
|
|
502
|
+
): JSX.Element | string {
|
|
503
|
+
if (typeof node === "string") {
|
|
504
|
+
return node;
|
|
505
|
+
}
|
|
506
|
+
// you can map the classes to something else here
|
|
507
|
+
const className = node.classes.join(" ");
|
|
508
|
+
const tag = node.tag; // one of: "img" | "a" | "ul" | "ol" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "br" | "p" | "li" | "span"
|
|
509
|
+
|
|
510
|
+
// Example of rendering img with MyOwnImageComponent:
|
|
511
|
+
if (tag === "img") {
|
|
512
|
+
return <MyOwnImageComponent {...node} />;
|
|
513
|
+
}
|
|
514
|
+
return React.createElement(
|
|
515
|
+
tag,
|
|
516
|
+
{
|
|
517
|
+
key,
|
|
518
|
+
className,
|
|
519
|
+
},
|
|
520
|
+
"children" in node ? node.children.map(build) : null
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
return <div {...val.attrs(root)}>{root.children.map(build)}</div>;
|
|
524
|
+
}
|
|
525
|
+
type MyRichTextOptions = AnyRichTextOptions; // you can reduce the surface of what you need to render, by restricting the `options` in `s.richtext(options)`
|
|
526
|
+
```
|
|
527
|
+
|
|
401
528
|
## Image
|
|
402
529
|
|
|
403
530
|
### Image Schema
|
|
@@ -418,7 +545,19 @@ export const schema = s.image();
|
|
|
418
545
|
export default c.define("/image", schema, c.file("/public/myfile.jpg"));
|
|
419
546
|
```
|
|
420
547
|
|
|
421
|
-
**NOTE**: This will not validate, since images requires `width`, `height` and a `sha256` checksum. You can fix
|
|
548
|
+
**NOTE**: This will not validate, since images requires `width`, `height` and a `sha256` checksum. You can fix validation errors like this by using the CLI or by using the VS Code plugin.
|
|
549
|
+
|
|
550
|
+
### Rendering images
|
|
551
|
+
|
|
552
|
+
The `ValImage` component is a wrapper around `next/image` that accepts a Val `Image` type.
|
|
553
|
+
|
|
554
|
+
You can use it like this:
|
|
555
|
+
|
|
556
|
+
```tsx
|
|
557
|
+
const content = useVal(contentVal);
|
|
558
|
+
|
|
559
|
+
return <ValImage src={content.image} alt={content.alt} />;
|
|
560
|
+
```
|
|
422
561
|
|
|
423
562
|
### Using images in components
|
|
424
563
|
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { type ValConfig, type InitVal, type ValConstructor } from "@valbuild/core";
|
|
1
|
+
import { type ValConfig, type InitVal, type ValConstructor, RichText, AnyRichTextOptions, ValModule, SelectorSource, Json } from "@valbuild/core";
|
|
2
2
|
import { raw } from "./raw.js";
|
|
3
3
|
import { decodeValPathOfString } from "./decodeValPathOfString.js";
|
|
4
|
+
type ValAttrs = {
|
|
5
|
+
"data-val-path"?: string;
|
|
6
|
+
};
|
|
4
7
|
export declare const initVal: (config?: ValConfig) => InitVal & {
|
|
5
8
|
val: ValConstructor & {
|
|
6
9
|
raw: typeof raw;
|
|
7
|
-
|
|
10
|
+
attrs: <T extends ValModule<SelectorSource> | Json | RichText<AnyRichTextOptions>>(target: T) => ValAttrs;
|
|
11
|
+
unstable_decodeValPathOfString: typeof decodeValPathOfString;
|
|
8
12
|
};
|
|
9
13
|
};
|
|
14
|
+
export {};
|
|
@@ -33,6 +33,16 @@ function _interopNamespace(e) {
|
|
|
33
33
|
var core__namespace = /*#__PURE__*/_interopNamespace(core);
|
|
34
34
|
var NextImage__default = /*#__PURE__*/_interopDefault(NextImage);
|
|
35
35
|
|
|
36
|
+
function _typeof(o) {
|
|
37
|
+
"@babel/helpers - typeof";
|
|
38
|
+
|
|
39
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
|
40
|
+
return typeof o;
|
|
41
|
+
} : function (o) {
|
|
42
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
43
|
+
}, _typeof(o);
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
function toPrimitive(t, r) {
|
|
37
47
|
if ("object" != typeof t || !t) return t;
|
|
38
48
|
var e = t[Symbol.toPrimitive];
|
|
@@ -107,7 +117,28 @@ var initVal = function initVal(config) {
|
|
|
107
117
|
s: s,
|
|
108
118
|
c: c,
|
|
109
119
|
val: _objectSpread2(_objectSpread2({}, val), {}, {
|
|
110
|
-
|
|
120
|
+
attrs: function attrs(target) {
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
var anyTarget = target;
|
|
123
|
+
var path;
|
|
124
|
+
if (target === null) {
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
127
|
+
path = core.Internal.getValPath(anyTarget);
|
|
128
|
+
if (!path && _typeof(anyTarget) === "object") {
|
|
129
|
+
path = anyTarget["valPath"];
|
|
130
|
+
}
|
|
131
|
+
if (typeof anyTarget === "string") {
|
|
132
|
+
path = decodeValPathOfString(anyTarget);
|
|
133
|
+
}
|
|
134
|
+
if (path) {
|
|
135
|
+
return {
|
|
136
|
+
"data-val-path": path
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return {};
|
|
140
|
+
},
|
|
141
|
+
unstable_decodeValPathOfString: decodeValPathOfString,
|
|
111
142
|
raw: raw
|
|
112
143
|
}),
|
|
113
144
|
config: currentConfig
|
|
@@ -33,6 +33,16 @@ function _interopNamespace(e) {
|
|
|
33
33
|
var core__namespace = /*#__PURE__*/_interopNamespace(core);
|
|
34
34
|
var NextImage__default = /*#__PURE__*/_interopDefault(NextImage);
|
|
35
35
|
|
|
36
|
+
function _typeof(o) {
|
|
37
|
+
"@babel/helpers - typeof";
|
|
38
|
+
|
|
39
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
|
40
|
+
return typeof o;
|
|
41
|
+
} : function (o) {
|
|
42
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
43
|
+
}, _typeof(o);
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
function toPrimitive(t, r) {
|
|
37
47
|
if ("object" != typeof t || !t) return t;
|
|
38
48
|
var e = t[Symbol.toPrimitive];
|
|
@@ -107,7 +117,28 @@ var initVal = function initVal(config) {
|
|
|
107
117
|
s: s,
|
|
108
118
|
c: c,
|
|
109
119
|
val: _objectSpread2(_objectSpread2({}, val), {}, {
|
|
110
|
-
|
|
120
|
+
attrs: function attrs(target) {
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
var anyTarget = target;
|
|
123
|
+
var path;
|
|
124
|
+
if (target === null) {
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
127
|
+
path = core.Internal.getValPath(anyTarget);
|
|
128
|
+
if (!path && _typeof(anyTarget) === "object") {
|
|
129
|
+
path = anyTarget["valPath"];
|
|
130
|
+
}
|
|
131
|
+
if (typeof anyTarget === "string") {
|
|
132
|
+
path = decodeValPathOfString(anyTarget);
|
|
133
|
+
}
|
|
134
|
+
if (path) {
|
|
135
|
+
return {
|
|
136
|
+
"data-val-path": path
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return {};
|
|
140
|
+
},
|
|
141
|
+
unstable_decodeValPathOfString: decodeValPathOfString,
|
|
111
142
|
raw: raw
|
|
112
143
|
}),
|
|
113
144
|
config: currentConfig
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { initVal as initVal$1 } from '@valbuild/core';
|
|
1
|
+
import { initVal as initVal$1, Internal } from '@valbuild/core';
|
|
2
2
|
import * as core from '@valbuild/core';
|
|
3
3
|
export { core as expr };
|
|
4
4
|
export { FILE_REF_PROP, GenericSelector, Schema, VAL_EXTENSION, derefPatch } from '@valbuild/core';
|
|
@@ -9,6 +9,16 @@ import NextImage from 'next/image';
|
|
|
9
9
|
import { jsx } from 'react/jsx-runtime';
|
|
10
10
|
export { ValApp } from './ValApp-6827827a.esm.js';
|
|
11
11
|
|
|
12
|
+
function _typeof(o) {
|
|
13
|
+
"@babel/helpers - typeof";
|
|
14
|
+
|
|
15
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
|
16
|
+
return typeof o;
|
|
17
|
+
} : function (o) {
|
|
18
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
19
|
+
}, _typeof(o);
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
function toPrimitive(t, r) {
|
|
13
23
|
if ("object" != typeof t || !t) return t;
|
|
14
24
|
var e = t[Symbol.toPrimitive];
|
|
@@ -83,7 +93,28 @@ var initVal = function initVal(config) {
|
|
|
83
93
|
s: s,
|
|
84
94
|
c: c,
|
|
85
95
|
val: _objectSpread2(_objectSpread2({}, val), {}, {
|
|
86
|
-
|
|
96
|
+
attrs: function attrs(target) {
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
+
var anyTarget = target;
|
|
99
|
+
var path;
|
|
100
|
+
if (target === null) {
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
path = Internal.getValPath(anyTarget);
|
|
104
|
+
if (!path && _typeof(anyTarget) === "object") {
|
|
105
|
+
path = anyTarget["valPath"];
|
|
106
|
+
}
|
|
107
|
+
if (typeof anyTarget === "string") {
|
|
108
|
+
path = decodeValPathOfString(anyTarget);
|
|
109
|
+
}
|
|
110
|
+
if (path) {
|
|
111
|
+
return {
|
|
112
|
+
"data-val-path": path
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {};
|
|
116
|
+
},
|
|
117
|
+
unstable_decodeValPathOfString: decodeValPathOfString,
|
|
87
118
|
raw: raw
|
|
88
119
|
}),
|
|
89
120
|
config: currentConfig
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"next",
|
|
9
9
|
"react"
|
|
10
10
|
],
|
|
11
|
-
"version": "0.
|
|
11
|
+
"version": "0.58.0",
|
|
12
12
|
"scripts": {
|
|
13
13
|
"typecheck": "tsc --noEmit",
|
|
14
14
|
"test": "jest"
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"exports": true
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@valbuild/core": "~0.
|
|
49
|
-
"@valbuild/react": "~0.
|
|
50
|
-
"@valbuild/server": "~0.
|
|
51
|
-
"@valbuild/ui": "~0.
|
|
48
|
+
"@valbuild/core": "~0.58.0",
|
|
49
|
+
"@valbuild/react": "~0.58.0",
|
|
50
|
+
"@valbuild/server": "~0.58.0",
|
|
51
|
+
"@valbuild/ui": "~0.58.0",
|
|
52
52
|
"client-only": "^0.0.1",
|
|
53
53
|
"server-only": "^0.0.1"
|
|
54
54
|
},
|