Ga naar hoofdinhoud

CnWorkspaceFilterWidget

A dashboard choice list (radio-list or select) that writes the selected value into the page workspace context, so sibling widgets whose declarative sources interpolate @workspace.<key> re-resolve their tokens and refetch (#91 Wave 3 — the workspace-filter widget).

Registered as the workspace-filter dashboard widget type; resolved by its type key via CnDashboardPage's widget dispatcher. Not a public import — place it declaratively with type: "workspace-filter".

How it drives siblings​

CnDashboardPage provides a reactive cnWorkspaceContext bag. This widget writes the chosen value under the key named by writes ("@workspace.queue" → the bare key queue). Any sibling widget on the page — an object-table with filter: { queue: "@workspace.queue" }, a stat whose endpoint URL interpolates @workspace.queue, a chart aggregate filter — re-resolves its @workspace.* tokens and refetches when the selection changes. This is the pipelinq WerkplekQueueFilter contract: pick a queue, every work-list widget follows.

An optional leading "All" option (allLabel) writes the empty string, so an optional sibling token (@workspace.queue?) drops and the siblings show the unfiltered set.

Options — three sources​

ConfigBehaviour
options[]Static list — each { value, label, count? } (or { id, name } / bare strings, normalised).
source{ register, schema, groupBy, filter? } — OpenRegister's /grouped facet: each distinct value becomes an option with its object count.
endpointSource{ url, params?, responsePath? } — an app endpoint returning an array of options.

Config​

{
"widgetKey": "workspace-filter",
"content": {
"label": "Queue",
"writes": "@workspace.queue", // the workspace key to write
"style": "radio", // "radio" (default) | "select"
"allLabel": "All", // optional clear affordance
"showCounts": true,
"source": { "register": "pipelinq", "schema": "werkitem", "groupBy": "queue" }
// or: "options": [{ "value": "inbox", "label": "Inbox", "count": 3 }]
// or: "endpointSource": { "url": "/apps/pipelinq/api/queues" }
}
}

Selection precedence on mount: an existing workspace value → content.default → (when no allLabel) the first option, so siblings always have a value to filter on.

Start card and object picker​

A dashboard often opens with a "start" card: a title and one line of guidance on the left, the control that puts something in focus on the right (PqDashboard's "Contact starten"). Declare it on the same widget:

{
"id": "contact-start",
"type": "workspace-filter",
"showTitle": false,
"content": {
"layout": "start",
"title": "Contact starten",
"description": "Zoek de klant aan de lijn of aan de balie.",
"writes": "@workspace.selectedClient",
"objectSource": { "register": "pipelinq", "schema": "client", "labelField": "name", "placeholder": "Naam, telefoon, e-mail, postcode of klantnummer" },
"link": { "label": "Bekijk een voorbeeld", "route": { "name": "Help" } }
}
}
KeyMeaning
layout"start": title and description left, the control and link right; they wrap below each other on a narrow cell.
title, descriptionThe card's heading (17px, bold) and guidance line (muted, at most 420px wide). Translated through cnTranslate.
objectSource{ register, schema, labelField?, placeholder?, ariaLabel?, preload? }: the control becomes an OpenRegister object picker (CnResourceSelect) that searches on the server, so it reaches every object and not only a fetched page, and writes the picked object's id. With no selection, no default and no allLabel it selects the first object the source lists (one _limit=1 read), so the page always has one in focus. Its accessible name is ariaLabel, else the title.
link{ label, route? } or { label, href? }, drawn after the control.

objectSource wins over options, source and endpointSource. Without these keys the widget renders exactly as before.

Events​

EventPayloadDescription
change{ key, value }Emitted after a selection (in addition to the workspace-context write).

Reference (auto-generated)​

OpenRegister reliance​

heavy: this component cannot work without OpenRegister. 3 direct references, 12 files reached through imports.

Through: CnResourceSelect.

See the reliance overview for the filterable list.

Props​

NameTypeRequiredDefaultDescription
contentunion\{\}Persisted configuration blob. Start card keys (abstract-start-card): layout: "start" draws title and description beside the control, and link ({ label, route? | href? }) after it. objectSource ({ register, schema, labelField?, placeholder?, ariaLabel?, preload? }) makes the control an OpenRegister object picker that searches on the server and writes the picked object's id; without a selection and without allLabel it selects the first object. Chips (screens-page-lenses): style: "chips" draws the options as one compact segmented group with a count after each label. An option's countFrom ({ register, schema, filter? }, or a list of them that are added up) counts that list; a static count is used otherwise. urlKey keeps the choice in the address (?&lt;urlKey&gt;=&lt;value&gt;), and the address wins over default on load. Page controls (screens-page-controls): style: "toggle" is a switch that writes onValue / offValue (true / false); layout: "bar" with controls draws several controls side by side in one row, and align: "end" pushes a control to the right edge.

Events​

NamePayloadDescription
change—Emitted when the selection changes. Payload: { key, value }.

Chips and page lenses​

A dashboard can offer chips that show one kind of work at a time (PqMijnWerk: Alle 9, Tickets 7, Leads 1, Opvolgingen 1). Declare the chips on this widget and let each widget say which choices show it.

{
"id": "lens",
"type": "workspace-filter",
"content": {
"style": "chips",
"label": "Show", // the group's accessible name
"writes": "@workspace.lens",
"urlKey": "lens", // ?lens=tickets, wins over default on load
"default": "all",
"options": [
{ "value": "all", "label": "All",
"countFrom": [ { "register": "app", "schema": "ticket", "filter": { "assignee": "@me" } },
{ "register": "app", "schema": "lead", "filter": { "assignee": "@me" } } ] },
{ "value": "tickets", "label": "Tickets",
"countFrom": { "register": "app", "schema": "ticket", "filter": { "assignee": "@me" } } }
]
}
}

countFrom is one list or several whose totals are added up (one list request each); an option whose count failed shows its label only. A static count works too.

A widget (or a kpiRow tile) is shown by a choice with a WORKSPACE visibleWhen:

{ "id": "my-leads", "type": "record-list", "visibleWhen": { "workspace": "lens", "op": "in", "value": ["all", "leads"] } }

It is answered at once from the page workspace, with no request. A widget whose condition does not hold gives up its grid cell and the rows below close up; edit mode shows every widget. A widget that reads a key nobody wrote is hidden, so give the chips a default. The condition is a top-level one: inside all or any it is not honoured on a dashboard.

Give the chips a full-width cell with showTitle: false to get the board's bar. The group is a radio group: the arrow keys move the choice and Tab leaves it in one step.

Toggle, select and bar​

A switch and a select are page controls too. style: "toggle" draws a switch that writes onValue (default true) when on and offValue (default false) when off, and starts off. layout: "bar" with controls draws several controls in one row, each a workspace filter of its own; align: "end" pushes a control to the right edge.

{
"id": "my-bar", "type": "workspace-filter", "showTitle": false,
"content": {
"layout": "bar",
"controls": [
{ "style": "chips", "label": "Show", "writes": "@workspace.lens", "urlKey": "lens", "default": "all", "options": [] },
{ "style": "toggle", "label": "Show completed", "writes": "@workspace.showDone", "urlKey": "done", "align": "end" },
{ "style": "select", "label": "Sort", "writes": "@workspace.sort", "default": "deadline",
"options": [ { "value": "deadline", "label": "Deadline first" }, { "value": "priority", "label": "Priority first" } ] }
]
}
}

A list says what the controls mean for it with pageControls (see the record list and object table pages).