@valbuild/next 0.56.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
- ![Visual editing](./docs/visual-editing.png)
76
+ ![Visual editing](https://val.build/docs/images/overlay.png)
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
- ![Type check error](./docs/type-check-error.png)
89
+ ![Type check error](https://val.build/docs/images/type-check-error.png)
90
90
  - content can be refactored (change names, etc) just as if it was hard-coded (because it is)
91
- ![Renaming](./docs/renaming.gif)
91
+ ![Renaming](https://val.build/docs/images/renaming.gif)
92
92
  - works as normal with your **favorite IDE** without any plugins: search for content, references to usages, ...
93
- ![References](./docs/references.gif)
93
+ ![References](https://val.build/docs/images/references.gif)
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
@@ -228,7 +228,7 @@ In client components you can access your content with the `useVal` hook:
228
228
  // ./src/app/page.tsx
229
229
  "use client";
230
230
  import { NextPage } from "next";
231
- import { useVal } from "@valbuild/next/client";
231
+ import { useVal } from "./val/val.client";
232
232
  import contentVal from "./content.val";
233
233
 
234
234
  const Page: NextPage = () => {
@@ -379,7 +379,7 @@ You can use the `ValRichText` component to render content.
379
379
  "use client";
380
380
  import { ValRichText } from "@valbuild/next";
381
381
  import contentVal from "./content.val";
382
- import { useVal } from "@valbuild/next/client";
382
+ import { useVal } from "./val/val.client";
383
383
 
384
384
  export default function Page() {
385
385
  const content = useVal(contentVal);
@@ -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 this validation in the UI by opening the image and clicking the Fix button.
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
- decodeValPathOfString: typeof decodeValPathOfString;
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
- decodeValPathOfString: decodeValPathOfString,
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
- decodeValPathOfString: decodeValPathOfString,
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
- decodeValPathOfString: decodeValPathOfString,
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.56.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.56.0",
49
- "@valbuild/react": "~0.56.0",
50
- "@valbuild/server": "~0.56.0",
51
- "@valbuild/ui": "~0.56.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
  },