Add a preview widget which enables editors to easily switch from previewing to editing. Enable by setting widget to true:
<cms.previews widget />
Add a function to retrieve the current logged in user:
console.log(await cms.user())
.css
files used for custom fields and views..css
and .module.css
in custom fields and views.isContainer
optional if contains
is used on Types.Field validation (#369)
Introduces two new Field options available for every Field: required
and
validate
. The required
option will make sure the field value is not empty
when saving. The validate
option can be used to validate the field value
using a custom function. The function should return true
if the value is
valid, false
if it is not valid and a string if it is not valid and a
message should be shown to the user.
This is a breaking change, removing the optional
property from Fields.
It was never functional.
alinea.text('Hello field', {
help: 'This field only accepts "hello" as a value',
validate(value) {
if (value !== 'hello') return 'Only "hello" is allowed!'
}
})
condition
option are now constrained with their localeFix storing extra fields
on the Link field correctly for multiple links.
In conditional configuration functions it's now possible to access fields from
parent contexts. For example field options of a nested field inside a List
field can depend on the value of a field in the entry root.
const innerField = alinea.text('Read-only if status is published')
const Type = alinea.type('Conditional example', {
status: alinea.select('Status', {
draft: 'Draft',
published: 'Published'
}),
list: alinea.list('List', {
schema: {ListRow: alinea.type({innerField})}
})
})
alinea.track.options(innerField, get => {
return {readOnly: get(Type.status) === 'published'}
})
Entry
fields showing up as type unkown
in TypeScript.readOnly
option that is included in all fields will now show a lock item
next to the field label. The option is passed down in nested fields such as
the List
and Rich text
fields.The interval at which Alinea polls the backend for content updates is now configurable. It can be set in config.syncInterval and overwritten per query.
// Poll for updates every 60 seconds
const results = await cms.syncInterval(60).find(Entry())
// Disable syncing all for this query in case you want results fast,
// but not necessarily up to date
const matches = await cms.disableSync().find(Entry().search('fast'))
Pages can be queried with search terms.
Any (rich) text field with the searchable
option set to true
is indexed.
const results = await cms.find(Page().search('search', 'terms'))
Shared fields (#365)
Introduce the shared option for Fields. Fields can be persisted over all
locales if your content is localised by setting the shared
option to true
.
When the entry is published the field data is copied to other locales.
This is currently only supported on the root level, not on nested fields.
const Type = alinea.type('Persist', {
// Persist field data over all locales
sharedField: alinea.text('Shared text', {shared: true})
})
Conditional configuration (#364)
All field configuration can be adjusted based on the value of other fields. After defining fields in a Type a tracker function can be set up. The tracker function takes a reference to a field and a subscription function. In the subscription function field values can be retrieved and new options returned.
const Example = alinea.type('Conditional example', {
textField: alinea.text('Text field'),
readOnly: alinea.check('Make read-only'),
hidden: alinea.check('Hide field')
})
alinea.track.options(Example.textField, get => {
const textField = get(Example.textField)
const readOnly = get(Example.readOnly)
const hidden = get(Example.hidden)
return {
readOnly,
hidden,
help: `Text has ${textField.length} characters`
}
})
Preview
and BrowserPreview
exports from the alinea
package
to @alinea/preview
. This should help import the alinea config within in
restrictive environments such as Next 13 which will throw a compile error if
any browser code is imported. This will likely be tweaked in future releases.@alinea/preview/remix
preview hookGenerate types (#271)
Up until now the TypeScript definitions that were available for the content schema were fully inferred using the TypeScript compiler. While this had some advantages it also came with stability issues and overall did not prove to be the best solution. TypeScript definitions are now generated from the schema at build time. The runtime type information should prove useful for the upcoming GraphQL support as well. Since GrapQL does not come with namespacing we've introduced a few breaking changes:
The config file now supports a single schema at the root level, meaning the schema is used for every workspace.
The generated package structure in turn became simpler because the workspace distinction is no longer needed:
// Init pages now available from /pages
import {initPages} from '@alinea/generated/pages'
// The Page type describes every content type of the schema
// type Page = Page.TypeA | Page.TypeB
import {Page} from '@alinea/generated'
Remix run support (#273)
A few changes were necessary to get started with Remix. These changes should make it easier to work with other frameworks as well.
serve
instance if it is running. Since
Remix does not watch file changes in node modules this should make sure
you're always viewing the latest changes.serve
and generate
commands by avoiding race
conditions while publishingNEXT_PUBLIC_
, PUBLIC_
, VITE_
or GATSBY_
.alinea serve
command will apply publish actions directly to the memory
store instead of relying on the file watcher. This should result in better
performance.Pages.whereRoot
method which did not use the new alinea.root
location--fix
option to the generate command, which will write back any
missing or incorrect properties to disk.@alinea/css
package. Build outputs are now cached in the ci step using wireit but this
file was not included.The url
property of entries can now be controlled using the entryUrl
function in the type options. Urls are computed during generation and this can
help to keep them constant if you're using a web framework that does file
system routing. The available paramters are path
, parentPaths
and
locale
.
For example: making sure a doc page always has an url in
the form of /doc/$path
you can specify entryUrl
as the following:
type('Doc', {...fields}).configure({
entryUrl({path}) {
return `/doc/${path}`
}
})
The iframe used in the BrowserPreview
component now supports top level
navigation so it becomes possible to link the user to a cms route from within.
(#246)
The index of newly created entries will be based on the first child of parent. This makes them consistently sortable when published. (#241)
The exports of the alinea package are restructured. This is a breaking change because the input fields are no longer exposed directly but bundled in the "alinea" namespace. A few less used exports were removed and can be found in the @alinea packages.
Client code is shielded from being included server side when compiling with the "worker" condition enabled.
Initial support for selecting external links in the link field. The RichText ui component is adjusted to correctly render links. A custom component or tag can be passed to render links.
<RichText a={<a className="custom-link" />} doc={doc} />
<RichText a={CustomLinkComponent} doc={doc} />
typeNamespace
. It did not take the namespace into account, but
does now.The alinea cli will now forward commands placed after the serve or generate commands. It will wait until the alinea package is generated before doing so to make sure userland code can always depend on the package being available. It also simplifies running the dashboard and development server without requiring tools like npm-run-all. In practice, for a next.js website, this means one can configure scripts like so:
{
"scripts": {
"dev": "alinea serve -- next dev",
"build": "alinea generate -- next build"
}
}
Added a button to mark text as small within the rich text editor
New UI buttons to insert rows in any position in a list field
User preferences get a dedicated popover
Previews can register a listener and implement their own refetch mechanism. The communication happens via messages which work cross-origin.
// For any environment
import {registerPreview} from 'alinea/preview'
registerPreview({
refetch() {
// Reload server data
}
})
// A react hook is available
import {usePreview} from 'alinea/preview/react'
const {isPreviewing} = usePreview({
refetch() {
// Reload server data & redraw
}
})
// A hook specifically for next.js, which refetches static/server props
import {useNextPreview} from 'alinea/preview/next'
const {isPreviewing} = useNextPreview()
alinea
object that bundles the previously
exported config functions.A RichText react component is exposed from @alinea/ui to render rich text values
Auto-close navigation sidebar only for small screens (< 768px)
Update number field styles to use updated css variable names
Select fields configuration can now be set using the configure
method. This
helps type inference for the initial value.
select('Level', {
info: 'Info',
warning: 'Warning'
}).configure({
initialValue: 'info'
})