@valbuild/next 0.33.0 → 0.34.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.
Files changed (2) hide show
  1. package/README.md +296 -274
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -1,9 +1,40 @@
1
1
  <p align="center">
2
- <h1 align="center">Val</h1>
3
2
  <p align="center">
4
- <a href="https://app.val.build">https://app.val.build</a> ✨
3
+ <a href="https://app.val.build">
4
+ <svg width="100" height="100" viewBox="0 0 944 944" fill="none" xmlns="http://www.w3.org/2000/svg">
5
+ <circle cx="472" cy="472" r="472" fill="#1D1C28"/>
6
+ <g filter="url(#filter0_d_14_601)">
7
+ <path d="M181 348C181 345.791 182.791 344 185 344H320C322.209 344 324 345.791 324 348V602C324 604.209 322.209 606 320 606H185C182.791 606 181 604.209 181 602V348Z" fill="#38CD80"/>
8
+ </g>
9
+ <g filter="url(#filter1_i_14_601)">
10
+ <circle cx="252" cy="550" r="24" fill="#1E1F2A"/>
11
+ </g>
12
+ <path d="M659.085 550.374H658.585H654.427C652.095 550.374 650.434 549.729 649.347 548.522C648.25 547.306 647.658 545.431 647.658 542.807V439.857C647.658 437.924 646.091 436.357 644.158 436.357H629.16C627.227 436.357 625.66 437.924 625.66 439.857V455.828C625.66 456.658 624.986 457.332 624.155 457.332C623.593 457.332 623.072 457.015 622.798 456.508C618.502 448.559 612.704 442.661 605.399 438.838C597.962 434.671 589.622 432.592 580.394 432.592C571.897 432.592 563.846 434.128 556.247 437.2C548.643 440.274 541.944 444.796 536.154 450.761L536.153 450.761C530.537 456.552 526.106 463.693 522.854 472.174C519.598 480.668 517.975 490.411 517.975 501.395V505.697C517.975 516.86 519.597 526.693 522.854 535.187C526.105 543.667 530.535 550.895 536.148 556.864L536.153 556.869L536.159 556.875C541.95 562.659 548.647 567.088 556.246 570.161L556.256 570.165C563.856 573.057 572.083 574.5 580.932 574.5C589.456 574.5 597.527 572.325 605.137 567.982C612.625 563.807 618.519 557.469 622.822 548.992C623.085 548.475 623.609 548.147 624.176 548.147H624.546C625.161 548.147 625.66 548.646 625.66 549.261C625.66 555.468 627.583 560.617 631.452 564.665L631.451 564.665L631.46 564.673C635.511 568.72 640.668 570.735 646.889 570.735H658.585H659.085H661.157H661.657H760C761.933 570.735 763.5 569.168 763.5 567.235V553.874C763.5 551.941 761.933 550.374 760 550.374H733.542C732.161 550.374 731.042 549.255 731.042 547.874V385C731.042 383.067 729.475 381.5 727.542 381.5H680.701C678.768 381.5 677.201 383.067 677.201 385V398.361C677.201 400.294 678.768 401.861 680.701 401.861H706.543C707.924 401.861 709.043 402.981 709.043 404.361V547.874C709.043 549.255 707.924 550.374 706.543 550.374H661.657H661.157H659.085ZM600.117 550.146L600.111 550.149C594.977 552.448 589.304 553.601 583.086 553.601C570.468 553.601 560.194 549.435 552.222 541.12C544.436 532.633 540.512 520.847 540.512 505.697V501.395C540.512 494.274 541.581 487.79 543.712 481.936L543.714 481.931C545.849 475.89 548.778 470.842 552.495 466.775C556.398 462.521 560.92 459.246 566.061 456.944C571.195 454.645 576.867 453.492 583.086 453.492C589.117 453.492 594.696 454.731 599.829 457.207L599.838 457.211L599.848 457.215C605.166 459.517 609.681 462.79 613.4 467.035L613.4 467.035L613.408 467.044C617.306 471.292 620.324 476.431 622.458 482.469L622.459 482.474C624.59 488.328 625.66 494.812 625.66 501.933V505.16C625.66 512.46 624.59 519.125 622.458 525.159C620.324 531.022 617.393 536.075 613.669 540.326C609.95 544.571 605.435 547.844 600.117 550.146ZM464.902 570.735C466.39 570.735 467.716 569.794 468.206 568.389L512.685 441.011C513.479 438.736 511.79 436.357 509.38 436.357H491.006C489.496 436.357 488.157 437.325 487.683 438.758L447.951 558.864C447.716 559.575 447.051 560.055 446.303 560.055C445.554 560.055 444.89 559.575 444.655 558.864L404.923 438.758C404.449 437.325 403.109 436.357 401.6 436.357H383.225C380.815 436.357 379.126 438.736 379.921 441.011L424.399 568.389C424.89 569.794 426.215 570.735 427.704 570.735H464.902Z" fill="white" stroke="white"/>
13
+ <defs>
14
+ <filter id="filter0_d_14_601" x="127.464" y="290.464" width="250.072" height="369.072" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
15
+ <feFlood flood-opacity="0" result="BackgroundImageFix"/>
16
+ <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
17
+ <feOffset/>
18
+ <feGaussianBlur stdDeviation="26.768"/>
19
+ <feComposite in2="hardAlpha" operator="out"/>
20
+ <feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.803922 0 0 0 0 0.501961 0 0 0 0.3 0"/>
21
+ <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_14_601"/>
22
+ <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_14_601" result="shape"/>
23
+ </filter>
24
+ <filter id="filter1_i_14_601" x="228" y="526" width="48" height="48" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
25
+ <feFlood flood-opacity="0" result="BackgroundImageFix"/>
26
+ <feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
27
+ <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
28
+ <feOffset/>
29
+ <feGaussianBlur stdDeviation="6"/>
30
+ <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
31
+ <feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.803922 0 0 0 0 0.501961 0 0 0 0.3 0"/>
32
+ <feBlend mode="normal" in2="shape" result="effect1_innerShadow_14_601"/>
33
+ </filter>
34
+ </defs>
35
+ </svg>
36
+ </a>
5
37
  <br/>
6
- hard-coded content - super-charged
7
38
  </p>
8
39
  </p>
9
40
 
@@ -11,57 +42,117 @@
11
42
 
12
43
  Val is PRE-ALPHA - MOST features are broken and in state of flux.
13
44
 
14
- This is released only for **INTERNAL** **TESTING** PURPOSES.
45
+ This is release is only for **INTERNAL** **TESTING** PURPOSES.
15
46
 
16
47
  ## Table of contents
17
48
 
18
- - [Table of contents](#table-of-contents)
19
49
  - [Introduction](#introduction)
20
50
  - [Installation](#installation)
21
51
  - [Getting started](#getting-started)
22
- - [Concepts](#concepts)
23
- - [String](#string)
24
- - [Number](#number)
25
- - [Boolean](#boolean)
26
- - [Optional](#optional)
27
- - [Array](#array)
28
- - [Object](#object)
29
- - [Rich text](#richtext)
30
- - [Image](#image)
31
- - [Internalization](#internalization)
32
- - [Union](#union)
33
- - [One-of references](#one-of-references)
34
- - [Remote](#remote)
35
- - [Selector](#selector)
52
+ - [Schema types](#schema-types):
53
+ - [String](#string)
54
+ - [Number](#number)
55
+ - [Boolean](#boolean)
56
+ - [Optional](#optional)
57
+ - [Array](#array)
58
+ - [Record](#record)
59
+ - [Object](#object)
60
+ - [Rich text](#richtext)
61
+ - [Image](#image)
62
+ - [keyOf](#keyof)
36
63
 
37
64
  ## Introduction
38
65
 
39
- Val treats your content as code, but it remains fully editable.
66
+ Val is a CMS where **content** is **code** stored in your git repo.
40
67
 
41
- Val is built on top of TypeScript and Git, letting you use types, branches, version control (even rollbacks!) seamlessly.
68
+ As a CMS, Val is useful because:
42
69
 
43
- - Val is **TypeSafe**: being TypeScript-first means you see errors as you type. In addition you can **refactor**, find references and auto-complete content - just as if it was hard-coded. Safety means no more "add-only" content models, where your content model continues to expand because you hesitate to remove anything.
70
+ - editors can **change content** without having to ask developers to do it for them (and nobody wants that)
71
+ - a **well-documented** way to **structure content**
72
+ - **images** support is built-in
73
+ - **richtext** support is built-in
74
+ - built-in **visual editing** which lets editors click-then-edit content (and therefore **code**!) directly in your app
44
75
 
45
- - Version **control**: content is tied to commits, which means you can use branches and reverts just as you would normally do.
76
+ ![Visual editing](./docs/visual-editing.png)
46
77
 
47
- - Val is **isomorphic**: if your content scales beyond your code base, Val lets you easily make it remote. Just add `.remote()` to your schema and Val will host your content - your components stays the same. Remote content are handled using immutable references, so remote content are tied to Git commits - rollbacks, branches works as before.
78
+ <details>
79
+ <summary>Definition: editor</summary>
80
+ An editor in this context, is a non-technical person that edits content in your application (technical writer, proof-reader, legal, ...).
81
+ </details>
48
82
 
49
- - Contextual editing: Val is built from the ground up to support contextual editing. Editors can see Val content in the context of the app, and make edits directly there.
83
+ <br />
50
84
 
51
- - Full CMS: even hard-coded content has its drawbacks: i18n, images, rich text - Val supports all of these.
85
+ But, with the benefits of **hard-coded** content:
52
86
 
53
- - No signup required: **your** content is yours. Local development requires no sign ups and is free. When you are ready and editors needs access, you can sign up and without any changes to your code base. Head over to [https://app.val.build](https://app.val.build) to get started 🚀.
87
+ - works seamlessly **locally** or with git **branches**
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)
90
+ - content can be refactored (change names, etc) just as if it was hard-coded (because it is)
91
+ ![Renaming](./docs/renaming.gif)
92
+ - works as normal with your **favorite IDE** without any plugins: search for content, references to usages, ...
93
+ ![References](./docs/references.gif)
94
+ - **no** need for **code-gen** and extra build steps
95
+ - **fast** since the content is literally hosted with the application
96
+ - content is **always there** and can never fail (since it is not loaded from somewhere)
97
+ - no need to manage different **environments** containing different versions of content
98
+ - **resolve conflicts** like you normally resolve conflicts: **in git**
99
+
100
+ Compared to other CMSs, Val has the following advantages:
101
+
102
+ - **easy** to setup and to _grok_: Val is designed to have a minimum of boilerplate and there's **0** query languages to learn. If you know your way around JSON that's enough (if you don't you might want to learn it)
103
+ - **no signup** required to use it locally
104
+ - **no fees** for content that is in your code: your content is your code, and your code is... yours
105
+ - **minimal** API surface: Val is designed to not "infect" your code base
106
+ - **easy to remove**: since your content is already in your code and Val is designed to have a minimal surface, it's easy to remove if you want to switch
107
+
108
+ <details>
109
+ <summary>Upcoming feature: <strong>i18n</strong></summary>
110
+ Val will soon have support for i18n. Follow this repository to get notified when this is the case.
111
+ </details>
112
+
113
+ <details>
114
+ <summary>Upcoming feature: <strong>remote content</strong></summary>
115
+ Having hard-coded content is great for landing pages, product pages and other pages where the amount of content is manageable.
116
+
117
+ However, checking in the 10 000th blog entry in git might feel wrong (though we would say it is ok).
118
+
119
+ Therefore, Val will add `remote content` support which enables you to seamlessly move content to the cloud and back again as desired.
120
+ You code will still be the one truth, but the actual content will be hosted on [val.build](https://val.build).
121
+
122
+ `.remote()` support will also make it possible to have remote images to avoid having to put them in your repository.
123
+
124
+ There will also be specific support for remote i18n, which will make it possible to split which languages are defined in code, and which are fetched from remote.
125
+
126
+ More details on `.remote()` will follow later.
127
+
128
+ </details>
129
+
130
+ ## When to NOT use Val
131
+
132
+ Val is designed to work well on a single web-app, and currently only supports Next 13.4+ (more meta-frameworks will supported) and GitHub (more Git providers will follow).
133
+
134
+ Unless your application fits these requirements, you should have a look elsewhere (at least for now).
135
+
136
+ In addition, if you have a "content model", i.e. content schemas, that rarely change and you plan on using them in a lot of different applications (web, mobile, etc), Val will most likely not be a great fit.
137
+
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
+
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
+
142
+ ## Examples
143
+
144
+ Check out this README or the [examples](./examples) directory for examples.
54
145
 
55
146
  ## Installation
56
147
 
57
- - Make sure you have TypeScript 4.9+, Next 12+ (other meta frameworks will come), React 18+ (other frontend frameworks will come)
58
- - Install the packages:
148
+ - Make sure you have TypeScript 5+, Next 13.4+ (other meta frameworks will come), React 18.20.+ (other frontend frameworks will come)
149
+ - Install the package:
59
150
 
60
151
  ```sh
61
- npm install @valbuild/next
152
+ npm install @valbuild/next@latest
62
153
  ```
63
154
 
64
- - Create your val.config.ts file. NOTE: this file should be in the same directory as `tsconfig.json`:
155
+ - Create a `val.config.ts` file. **NOTE**: this file should be in the same directory as `tsconfig.json`:
65
156
 
66
157
  ```ts
67
158
  // ./val.config.ts
@@ -73,127 +164,170 @@ const { s, val } = initVal();
73
164
  export { s, val };
74
165
  ```
75
166
 
76
- - Enable contextual editing: setup Val endpoints
167
+ - Create the endpoints file:
77
168
 
78
169
  ```ts
79
170
  // ./src/pages/api/val/[...val].ts
80
171
 
81
- import { valEditHandler } from "../../../../val.config";
172
+ import { createRequestListener } from "@valbuild/server";
82
173
  import { NextApiHandler } from "next";
83
174
 
84
- const handler: NextApiHandler = valEditHandler;
175
+ const handler: NextApiHandler = createRequestListener("/api/val", {
176
+ valConfigPath: "./val.config",
177
+ });
85
178
 
86
179
  export default handler;
87
180
 
88
181
  export const config = {
89
182
  api: {
183
+ responseLimit: false,
90
184
  bodyParser: false,
91
185
  externalResolver: true,
92
186
  },
93
187
  };
94
188
  ```
95
189
 
96
- - Enable contextual editing: Use the Val provider in the \_app file:
190
+ - Use the Val provider in the root layout file:
97
191
 
98
192
  ```tsx
99
- // ./src/pages/_app.tsx
193
+ // ./app/layout.tsx
100
194
 
101
- import { ValProvider } from "@valbuild/react";
102
- import type { AppProps } from "next/app";
195
+ import { ValProvider } from "@valbuild/next";
103
196
 
104
- function MyApp({ Component, pageProps }: AppProps) {
197
+ export default function RootLayout({
198
+ children,
199
+ }: {
200
+ children: React.ReactNode;
201
+ }) {
105
202
  return (
106
- <ValProvider host="/api/val">
107
- <Component {...pageProps} />
108
- </ValProvider>
203
+ <html lang="en">
204
+ <body className={inter.className}>
205
+ <ValProvider> {children}</ValProvider>
206
+ </body>
207
+ </html>
109
208
  );
110
209
  }
210
+ ```
211
+
212
+ ### [OPTIONAL]: Setup eslint
213
+
214
+ Install the eslint package:
215
+
216
+ ```bash
217
+ npm install @valbuild/eslint-plugin@latest
218
+ ```
111
219
 
112
- export default MyApp;
220
+ Add the following to your `.eslintrc.json`:
221
+
222
+ ```json
223
+ "plugins": ["@valbuild"],
224
+ "rules": {
225
+ "@valbuild/no-illegal-module-ids": "error"
226
+ }
113
227
  ```
114
228
 
229
+ ### Add editor support
230
+
231
+ To make it possible to do non-local edits, head over to [val.build](https://val.build) and import your repository.
232
+
233
+ **NOTE**: your content is yours. No subscription (or similar) is required to host content from your repository.
234
+
235
+ If you do not need to edit content online (i.e. not locally), you do not need to sign up.
236
+
237
+ **WHY**: to update your code, we need to create a commit. This requires a server. We opted to create a service that does this easily, instead of having a self-hosted alternative, since time spent is money used. Also, the company behind [val.build](https://val.build) is the company that funds the development of this software.
238
+
115
239
  ## Getting started
116
240
 
117
241
  ### Create your first Val content file
118
242
 
119
- Content defined in Val is always defined `.val.{ts|js}` files.
243
+ Content in Val is always defined in `.val.ts` files.
244
+
245
+ **NOTE**: Val also works with `.js` files.
246
+
247
+ They must export a default `val.content` where the first argument equals the path of the file relative to the `val.config.ts` file.
248
+
249
+ **NOTE**: `val.ts` files are _evaluated_ by Val, therefore they have a specific set of requirements:
250
+
251
+ - They must have a default export that is `val.content`, they must have a `export const schema` with the Schema; and
252
+ - they CANNOT import anything other than `val.config` and `@valbuild/core`
120
253
 
121
- They must export a default `val.content` where the first argument equals the path of the file relative to the `val.config.{js|ts}` file.
254
+ ### Example of a `.val.ts` file
122
255
 
123
256
  ```ts
124
- // ./src/content/example/blogs.val.ts
257
+ // ./src/app/content.val.ts
125
258
 
126
259
  import { s, val } from "../../../val.config";
127
260
 
261
+ export const schema = s.object({
262
+ title: s.string().optional(), // <- NOTE: optional()
263
+ sections: s.array(
264
+ s.object({
265
+ title: s.string(),
266
+ text: s.richtext({
267
+ bold: true, // <- Enables bold in richtext
268
+ }),
269
+ })
270
+ ),
271
+ });
272
+
128
273
  export default val.content(
129
- "/src/content/example/blogs", // <- NOTE: this must be the same path as the file
130
- s.array(s.object({ title: s.string(), text: s.string() })),
131
- [
132
- {
133
- title: "Title 1",
134
- text: "Text 1",
135
- },
136
- {
137
- title: "Title 2",
138
- text: "Text 2",
139
- },
140
- ]
274
+ "/src/app/content", // <- NOTE: this must be the same path as the file
275
+ schema,
276
+ {
277
+ title: "My Page",
278
+ sections: [
279
+ {
280
+ title: "Section 1",
281
+ text: val.richtext`
282
+ RichText is **awesome**`,
283
+ },
284
+ ],
285
+ }
141
286
  );
142
287
  ```
143
288
 
144
289
  ### Use your content
145
290
 
146
- ```tsx
147
- // ./src/pages/example/index.tsx
291
+ In client components you can access your content with the `useVal` hook:
148
292
 
293
+ **NOTE**: Support for React Server Components and server side rendering will come soon.
294
+
295
+ ```tsx
296
+ // ./src/app/page.tsx
297
+ "use client";
149
298
  import { NextPage } from "next";
150
- import { useVal } from "@valbuild/react";
151
- import blogsVal from "@/content/example/blogs.val";
299
+ import { useVal } from "@valbuild/next/client";
300
+ import contentVal from "./content.val";
152
301
 
153
- const Blog: NextPage = () => {
154
- const blog = useVal(blogsVal[0]);
302
+ const Page: NextPage = () => {
303
+ const { title, sections } = useVal(contentVal);
155
304
  return (
156
305
  <main>
157
- <article>
306
+ {title && (
307
+ <section>
308
+ <h1>{title}</h1>
309
+ </section>
310
+ )}
311
+ {sections.map((section) => (
158
312
  <section>
159
- <h1>{blog.title}</h1>
160
- <p>{blog.text}</p>
313
+ <h2>{section.title}</h2>
314
+ <ValRichText
315
+ theme={{
316
+ bold: "font-bold",
317
+ }}
318
+ >
319
+ {section.text}
320
+ </ValRichText>
161
321
  </section>
162
- </article>
322
+ ))}
163
323
  </main>
164
324
  );
165
325
  };
166
326
 
167
- export default Blog;
327
+ export default Page;
168
328
  ```
169
329
 
170
- ## Concepts
171
-
172
- `.val.{ts|js}` files **MUST** have a default export which is a ValModule. A ValModule is a special type of [Selector](#selectors). Selectors makes it possible to select a subset of content from a ValModule or a another Selector.
173
-
174
- Selectors can be turned to Val types using `useVal` or `fetchVal`.
175
-
176
- ### .val files
177
-
178
- `.val.{ts|js}` files are the files in which you store your content.
179
-
180
- They are evaluated when the content is run, therefore they have a specific set of requirements. They must have a default export that is `val.content`, they must have a `export const schema` with the Schema and they CANNOT import anything other than `val.config` and `@valbuild/core`.
181
-
182
- Example:
183
-
184
- ```ts
185
- import { s, val } from "../val.config";
186
-
187
- export const schema = t.string();
188
-
189
- export default val.content(
190
- "/file/path/relative/to/val/config",
191
- schema,
192
- "My string content"
193
- );
194
- ```
195
-
196
- NOTE: IN THE FUTURE, they will be validated by the eslint-plugin.
330
+ # Schema types
197
331
 
198
332
  ## String
199
333
 
@@ -203,16 +337,6 @@ import { s } from "./val.config";
203
337
  s.string(); // <- Schema<string>
204
338
  ```
205
339
 
206
- ### String selectors
207
-
208
- See [Selectors](#selector) for more info.
209
-
210
- ### String `.eq`
211
-
212
- ```ts
213
- useVal(stringVal.eq("")); // <- Val<boolean>
214
- ```
215
-
216
340
  ## Number
217
341
 
218
342
  ```ts
@@ -221,16 +345,6 @@ import { s } from "./val.config";
221
345
  s.number(); // <- Schema<number>
222
346
  ```
223
347
 
224
- ### Number selectors
225
-
226
- See [Selectors](#selector) for more info.
227
-
228
- ### Number `.eq`
229
-
230
- ```ts
231
- useVal(numberVal.eq(2)); // <- Val<boolean>
232
- ```
233
-
234
348
  ## Boolean
235
349
 
236
350
  ```ts
@@ -239,16 +353,6 @@ import { s } from "./val.config";
239
353
  s.boolean(); // <- Schema<boolean>
240
354
  ```
241
355
 
242
- ### Boolean selectors
243
-
244
- See [Selectors](#selector) for more info.
245
-
246
- ### Boolean `.eq`
247
-
248
- ```ts
249
- useVal(booleanVal.eq(true)); // <- Val<boolean>
250
- ```
251
-
252
356
  ## Optional
253
357
 
254
358
  All schema types can be optional. An optional schema creates a union of the type and `null`.
@@ -259,34 +363,22 @@ import { s } from "./val.config";
259
363
  s.string().optional(); // <- Schema<string | null>
260
364
  ```
261
365
 
262
- ### Selectors
263
-
264
- ### Accessing the underlying type: `.andThen`
265
-
266
- To use and optional val, you can use the [.andThen](#andthen) selector.
267
-
268
366
  ## Array
269
367
 
270
368
  ```ts
271
- s.array(t.string());
369
+ s.array(t.string()); // <- Schema<string[]>
272
370
  ```
273
371
 
274
- ### Selecting arrays
275
-
276
- ### `.filter`
277
-
278
- TODO: text
372
+ ## Record
279
373
 
280
- ```ts
281
- useVal(myArray.filter((item) => item.title.eq("Title 1")));
282
- ```
374
+ The type of `s.record` is `Record`.
283
375
 
284
- ### `.map`
376
+ It is similar to an array, in that editors can add and remove items in it, however it has a unique key which can be used as, for example, the slug or as a part of an route.
285
377
 
286
- TODO:
378
+ **NOTE**: records can also be used with `keyOf`.
287
379
 
288
380
  ```ts
289
- useVal(myArray.map((item) => ({ test: item.title })));
381
+ s.record(t.number()); // <- Schema<Record<string, number>>
290
382
  ```
291
383
 
292
384
  ## Object
@@ -297,24 +389,23 @@ s.object({
297
389
  });
298
390
  ```
299
391
 
300
- ### Selecting objects
392
+ ## RichText
301
393
 
302
- You can select Selector objects almost as if they were normal objects. The exception is that you cannot use the spread (`...`) operator.
394
+ <details>
395
+ <summary>RichText in Val represented both in code and to the editors as <strong>semantic html5</strong>.</summary>
303
396
 
304
- Example:
397
+ This means that content will be accessible and according to spec out of the box. The flip-side is that Val will not support RichText that includes elements that is not part of the html 5 standard.
305
398
 
306
- ```ts
307
- useVal({ foo: myObjectVal.hello });
308
- ```
399
+ This opinionated approach was chosen since rendering anything, makes it hard for developers to maintain and hard for editors to understand.
309
400
 
310
- ## RichText
401
+ </details>
311
402
 
312
403
  ### RichText Schema
313
404
 
314
405
  ```ts
315
- import { s } from "./val.config";
316
-
317
- s.richtext();
406
+ s.richtext({
407
+ // options
408
+ });
318
409
  ```
319
410
 
320
411
  ### Initializing RichText content
@@ -324,54 +415,60 @@ To initialize some text content using a RichText schema, you can use follow the
324
415
  ```ts
325
416
  import { s, val } from "./val.config";
326
417
 
327
- // TODO: need some other way of doing this:
328
- export default val.content("/example/richtext.ts", s.richtext(), {
329
- children: [
330
- {
331
- type: "paragraph",
332
- version: 1,
333
- indent: 0,
334
- direction: "ltr",
335
- format: "",
336
- children: [
337
- {
338
- type: "text",
339
- version: 1,
340
- mode: "normal",
341
- format: 0,
342
- detail: 0,
343
- style: "",
344
- text: "TODO: update me",
345
- },
346
- ],
347
- },
348
- ],
349
- direction: "ltr",
350
- format: "",
351
- indent: 0,
352
- type: "root",
353
- version: 1,
418
+ export const schema = s.richtext({
419
+ // styling:
420
+ bold: true, // enables bold
421
+ //italic: true, // enables italic text
422
+ //lineThrough: true, // enables line/strike-through
423
+ // tags:
424
+ //headings: ["h1", "h2", "h3", "h4", "h5", "h6"], // sets which headings are available
425
+ //a: true, // enables links
426
+ //img: true, // enables images
427
+ //ul: true, // enables unordered lists
428
+ //ol: true, // enables ordered lists
354
429
  });
430
+
431
+ export default val.content(
432
+ "/src/app/content",
433
+ schema,
434
+ val.richtext`
435
+ NOTE: this is markdown.
436
+
437
+ **Bold** text.
438
+ `
439
+ );
355
440
  ```
356
441
 
357
- ### Render RichText
442
+ ### Rendering RichText
358
443
 
359
444
  You can use the `ValRichText` component to render content.
360
445
 
361
446
  ```tsx
362
447
  "use client";
363
- import { useVal, ValRichText } from "@valbuild/react";
364
- import richtextVal from "./richtext";
448
+ import { ValRichText } from "@valbuild/next";
449
+ import contentVal from "./content.val";
450
+ import { useVal } from "@valbuild/next/client";
365
451
 
366
452
  export default function Page() {
367
- const richtext = useVal(richtextVal);
368
- return <ValRichText>{richtext}</ValRichText>;
453
+ const content = useVal(contentVal);
454
+ return (
455
+ <main>
456
+ <ValRichText
457
+ theme={{
458
+ bold: "font-bold", // <- maps bold to a class. NOTE: tailwind classes are supported
459
+ //
460
+ }}
461
+ >
462
+ {content}
463
+ </ValRichText>
464
+ </main>
465
+ );
369
466
  }
370
467
  ```
371
468
 
372
469
  ## Image
373
470
 
374
- ### Schema
471
+ ### Image Schema
375
472
 
376
473
  ```ts
377
474
  s.image();
@@ -389,7 +486,7 @@ export const schema = s.image();
389
486
  export default val.content("/image", schema, val.file("/public/myfile.jpg"));
390
487
  ```
391
488
 
392
- 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.
489
+ **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.
393
490
 
394
491
  ### Using images in components
395
492
 
@@ -404,100 +501,25 @@ const image = useVal(imageVal);
404
501
  return <img src={image.url} />;
405
502
  ```
406
503
 
407
- ## Internalization
408
-
409
- **NOTE**: WORKS ONLY ON THE TYPE LEVEL
410
-
411
- To enable i18n, you must update your Val with the locales you want to enforce.
412
-
413
- Example:
414
-
415
- ```ts
416
- // ./val.config.ts
417
-
418
- import { initVal } from "@valbuild/core";
419
-
420
- const { s, val } = initVal({
421
- locales: {
422
- required: ["en_US", "fr_FR"],
423
- fallback: "en_US",
424
- },
425
- });
426
-
427
- export { s, val };
428
- ```
429
-
430
- ## Union
504
+ ## KeyOf
431
505
 
432
- **NOTE**: WORKS ONLY ON THE TYPE LEVEL
506
+ If you need to reference content in another `.val` file you can use the `keyOf` schema.
433
507
 
434
- TODO: fill in.
508
+ ### KeyOf Schema
435
509
 
436
510
  ```ts
437
- s.union(
438
- "type",
439
- s.object({ type: s.literal("type1"), bar: s.string() }),
440
- s.object({ type: s.literal("type2"), foo: s.number() })
441
- );
442
- ```
443
-
444
- ### Selecting unions
445
-
446
- ### `.fold`
447
-
448
- TODO: description
511
+ import otherVal from "./other.val"; // NOTE: this must be an array or a record
449
512
 
450
- ```ts
451
- useVal(myUnionVal.fold("type")({
452
-
453
- })
513
+ s.keyOf(otherVal);
454
514
  ```
455
515
 
456
- ## One of references
457
-
458
- **NOTE**: Currently not possible to change from UI
459
-
460
- ```ts
461
-
462
- ```
463
-
464
- # Remote
465
-
466
- **NOTE**: WORKS ONLY ON THE TYPE LEVEL
516
+ ### Initializing keyOf
467
517
 
468
- All schemas can be converted into `.remote`.
518
+ ### Using keyOf to reference content
469
519
 
470
- TODO: add description.
471
-
472
- Example:
473
-
474
- ```ts
475
- export const schema = s.object({ stuff: s.string() });
476
-
477
- export default val.content("/remote/example", schema, val.remote("REFERENCE"));
478
- ```
479
-
480
- ## Selector
481
-
482
- To select parts of a your content you should use Selectors.
483
- If you make the content `.remote`
484
-
485
- ### `.andThen`
486
-
487
- All selectors can use `andThen` method which is similar to the `&&` operator. You can use this to only do operations on optionals that are defined. NOTE: only TRUTHY arguments are passed in to `andThen` (i.e. the empty string, `''` is NOT truthy).
488
-
489
- Given the example schema:
490
-
491
- ```ts
492
- // ./maybeArray.val.ts
493
- //...
494
-
495
- export const schema = t.array(t.string()).optional();
496
-
497
- //...
498
- ```
520
+ ```tsx
521
+ const article = useVal(articleVal); // s.object({ author: s.keyOf(otherVal) })
522
+ const authors = useVal(otherVal); // s.array(s.object({ name: s.string() }))
499
523
 
500
- ```ts
501
- import maybeArrayVal from "./maybeArray.val";
502
- useVal(maybeArrayVal.andThen((array) => array.filter((v) => v.eq("foo"))));
524
+ const nameOfAuthor = authors[articleVal.author].name;
503
525
  ```
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "next",
9
9
  "react"
10
10
  ],
11
- "version": "0.33.0",
11
+ "version": "0.34.0",
12
12
  "scripts": {
13
13
  "typecheck": "tsc --noEmit",
14
14
  "test": "jest"
@@ -40,9 +40,9 @@
40
40
  "exports": true
41
41
  },
42
42
  "dependencies": {
43
- "@valbuild/core": "~0.33.0",
44
- "@valbuild/react": "~0.33.0",
45
- "@valbuild/server": "~0.33.0",
43
+ "@valbuild/core": "~0.34.0",
44
+ "@valbuild/react": "~0.34.0",
45
+ "@valbuild/server": "~0.34.0",
46
46
  "client-only": "^0.0.1",
47
47
  "server-only": "^0.0.1"
48
48
  },