Arches Component Lab
A Vue 3 / PrimeVue component library for building custom Arches applications.
Installation
pip install arches-component-lab
Project Configuration
-
If you do not already have an Arches project, create one by following the instructions in the Arches documentation.
-
Add
arches_querysetsandarches_component_labtoINSTALLED_APPSbelow the name of your project but abovearches. For Arches >= 8.x, also addpgtrigger:
INSTALLED_APPS = (
"my_project_name",
...
"arches_querysets",
"arches_component_lab",
"arches",
...
"pgtrigger",
)
- Add
arches_component_labas a dependency inpackage.json:
"dependencies": {
"arches_component_lab": "archesproject/arches-component-lab#main"
}
- Add the
arches_component_labURLs tourls.py:
urlpatterns = [
path("", include("arches_component_lab.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
- Start your project and install frontend dependencies:
python manage.py runserver
npm install && npm run build_development
- Check for missing
WidgetMappingrecords:
python manage.py validate --codes 2001 --verbosity 2
See Extending Arches Component Lab if any are missing.
Frontend API
Bootstrapping an app
import MyComponent from '@/my_project/MyComponent.vue';
import { createVueApplication } from '@/arches_component_lab/application';
createVueApplication({ component: MyComponent }).then(app => app.mount('#app'));
Widgets
GenericWidget looks up the widget mapped to a node (see Extending Arches Component Lab) and resolves the real component at runtime. In edit mode it wraps the resolved widget in a GenericFormField, which registers the node as a PrimeVue Forms FormField keyed by nodeAlias, ties its dirty/touched state and validation errors into an ancestor <Form>, and renders those errors:
<script setup lang="ts">
import { ref } from 'vue';
import GenericWidget from '@/arches_component_lab/generics/GenericWidget/GenericWidget.vue';
import type { WidgetMode } from '@/arches_component_lab/widgets';
import type { AliasedNodeData } from '@/arches_component_lab/generics';
const MODE: WidgetMode = 'edit';
const nodeData = ref<AliasedNodeData | null>(null);
</script>
<template>
<GenericWidget
node-alias="my_text_node"
graph-slug="my_graph"
:mode="MODE"
:aliased-node-data="nodeData"
@update:aliased-node-data="nodeData = $event"
/>
</template>
aliasedNodeData/value are both optional. Without either, GenericWidget falls back to the node's configured default (cardXNodeXWidgetData.config.defaultValue, part of the widget config it already fetches), so it renders from just graphSlug/nodeAlias/mode:
<template>
<GenericWidget
node-alias="my_text_node"
graph-slug="my_graph"
mode="edit"
/>
</template>
Importing a widget directly skips the runtime resolution and the FormField integration described above. The widget emits update:aliasedNodeData/update:value on its own (some widgets also emit update:isDirty); the caller wires that into any surrounding form:
<script setup lang="ts">
import { ref } from 'vue';
import { TextWidget } from '@/arches_component_lab/widgets';
import type { WidgetMode } from '@/arches_component_lab/widgets';
import type { StringAliasedNodeData } from '@/arches_component_lab/datatypes';
const MODE: WidgetMode = 'edit';
const nodeData = ref<StringAliasedNodeData | null>(null);
</script>
<template>
<TextWidget
:mode="MODE"
:aliased-node-data="nodeData"
@update:aliased-node-data="nodeData = $event"
/>
</template>
Cards
GenericCard renders a whole nodegroup. It fetches the tile (fetchTileData) and every node's widget config, then renders GenericCardEditor or GenericCardViewer depending on mode. The editor wraps a PrimeVue Form and renders one GenericWidget per node, and saves the collected tile with upsertTile — from its own save button, or by calling .save() on it directly (exposed via defineExpose).
<script setup lang="ts">
import { ref } from 'vue';
import GenericCard from '@/arches_component_lab/generics/GenericCard/GenericCard.vue';
import type { AliasedTileData } from '@/arches_component_lab/generics';
const tileData = ref<AliasedTileData>();
</script>
<template>
<GenericCard
graph-slug="my_graph"
nodegroup-alias="my_nodegroup"
:resource-instance-id="resourceInstanceId"
:tile-id="tileId"
:tile-data="tileData"
mode="edit"
@update:tile-data="tileData = $event"
@save="tileData = $event"
@reset="() => {}"
/>
</template>
Reference
@/arches_component_lab/widgets
| Name | Type | Description |
|---|---|---|
WidgetMode |
'edit' | 'view' | 'configure' |
The three states a widget can render in |
BaseWidgetProps |
{ mode: WidgetMode; nodeAlias?: string; graphSlug?: string } |
Props every widget accepts |
Every widget's aliasedNodeData/cardXNodeXWidgetData prop is typed to one specific datatype. Where no widget-specific cardXNodeXWidgetData type is listed, the widget uses the base CardXNodeXWidgetData (no extra config fields). Both columns import from @/arches_component_lab/datatypes:
| Widget | aliasedNodeData type |
cardXNodeXWidgetData type |
|---|---|---|
TextWidget, RichTextWidget |
StringAliasedNodeData |
StringCardXNodeXWidgetData |
NonLocalizedTextWidget |
NonLocalizedTextAliasedNodeData |
CardXNodeXWidgetData |
NumberWidget |
NumberAliasedNodeData |
NumberCardXNodeXWidgetData |
DatePickerWidget |
DateAliasedNodeData |
DateCardXNodeXWidgetData |
EDTFWidget |
EDTFAliasedNodeData |
CardXNodeXWidgetData |
ConceptSelectWidget |
ConceptAliasedNodeData |
CardXNodeXWidgetData |
ConceptRadioWidget |
ConceptAliasedNodeData |
ConceptCardXNodeXWidgetData |
ConceptMultiselectWidget |
ConceptListAliasedNodeData |
CardXNodeXWidgetData |
DomainSelectWidget, DomainRadioWidget |
DomainAliasedNodeData |
DomainCardXNodeXWidgetData |
DomainCheckboxWidget, DomainMultiselectWidget |
DomainListAliasedNodeData |
DomainCardXNodeXWidgetData |
LanguageSelectWidget |
LanguageAliasedNodeData |
CardXNodeXWidgetData |
RadioBooleanWidget, SwitchWidget |
BooleanAliasedNodeData |
BooleanCardXNodeXWidgetData |
ResourceInstanceSelectWidget |
ResourceInstanceAliasedNodeData |
ResourceInstanceCardXNodeXWidgetData |
ResourceInstanceMultiselectWidget |
ResourceInstanceListAliasedNodeData |
ResourceInstanceListCardXNodeXWidgetData |
FileListWidget |
FileListAliasedNodeData |
FileListCardXNodeXWidgetData |
NodeValueSelectWidget |
NodeValueAliasedNodeData |
CardXNodeXWidgetData |
URLWidget |
URLAliasedNodeData |
CardXNodeXWidgetData |
MapWidget |
GeoJSONFeatureCollectionAliasedNodeData |
MapCardXNodeXWidgetData |
@/arches_component_lab/generics
GenericWidget/GenericCard resolve their concrete component at runtime instead of being imported directly — that is the "generic" here, not a TypeScript <T>.
| Export | Description |
|---|---|
GenericCard |
Card editor/viewer for a nodegroup |
GenericWidget |
Single widget resolved from widget config |
GenericCardProps, GenericWidgetProps |
Props interfaces |
AliasedNodeData, AliasedTileData |
Node/tile value types |
GenericWidgetProps:
| Name | Type | Description |
|---|---|---|
graphSlug |
string |
Graph the node belongs to |
nodeAlias |
string |
Node to render |
mode |
WidgetMode |
'edit', 'view', or 'configure' |
aliasedNodeData |
AliasedNodeData | null |
Current value; takes priority over value |
value |
unknown |
Raw value fallback, used only if aliasedNodeData is omitted |
cardXNodeXWidgetData |
CardXNodeXWidgetData |
Pre-fetched widget config; skips GenericWidget's own fetch |
cardXNodeXWidgetDataOverrides |
Partial<CardXNodeXWidgetData> |
Merged into the fetched config after fetching |
isDirty |
boolean |
Marks the field dirty on the surrounding <Form> |
shouldShowLabel |
boolean |
Show the node's label (default true) |
GenericCardProps:
| Name | Type | Description |
|---|---|---|
graphSlug |
string |
Graph the nodegroup belongs to |
nodegroupAlias |
string |
Nodegroup to render |
mode |
WidgetMode |
'edit', 'view', or 'configure' |
resourceInstanceId |
string | null |
Resource this tile belongs to, for a new tile |
selectedNodeAlias |
string | null |
Node to focus within the card |
shouldShowFormButtons |
boolean |
Show the built-in save/reset buttons (default true) |
tileData |
AliasedTileData |
Pre-fetched tile; skips GenericCard's own fetch |
tileId |
string | null |
Tile to fetch when tileData is not provided |
@/arches_component_lab/datatypes
*AliasedNodeData types are listed in the widget table above. Supporting types:
| Export | Description |
|---|---|
LanguageValue |
Per-language string value ({ value: string; direction: 'ltr' | 'rtl' }) |
URLNodeValue |
URL node value ({ url: string; url_label: string }) |
FileReference |
File attachment reference |
ResourceInstanceReference |
Resource instance link reference |
DomainOption |
Domain value option |
@/arches_component_lab/application
| Name | Type |
|---|---|
createVueApplication |
(options: CreateVueApplicationOptions) => Promise<App> |
generateArchesURL |
(urlName: string, urlParameters?: Record<string, string | number>, queryParameters?: Record<string, string | number>, languageCode?: string) => string |
CreateVueApplicationOptions |
{ component: Component; themeConfiguration?: ArchesThemeConfiguration; initialProps?: Record<string, unknown> } |
@/arches_component_lab/themes
| Name | Type | Description |
|---|---|---|
DEFAULT_THEME |
ArchesThemeConfiguration |
Theme createVueApplication uses when themeConfiguration is not passed |
ArchesThemeConfiguration |
— | PrimeVue theme configuration shape |
Creating a Custom Widget
A widget is an editor/viewer pair plus a dispatcher that picks between them by mode. The example below, RatingWidget, is a new widget for the existing number datatype.
- Create
widgets/RatingWidget/with atypes.tsextendingBaseWidgetProps:
import type { BaseWidgetProps } from "@/arches_component_lab/widgets/types.ts";
import type {
NumberAliasedNodeData,
NumberCardXNodeXWidgetData,
} from "@/arches_component_lab/datatypes/number/types.ts";
export interface RatingWidgetProps extends BaseWidgetProps {
cardXNodeXWidgetData?: NumberCardXNodeXWidgetData;
aliasedNodeData?: NumberAliasedNodeData | null;
value?: number | null;
}
- Add
RatingWidget.vue, the dispatcher — switches onmode, re-emitsupdate:aliasedNodeData,update:value,initialized:
<script setup lang="ts">
import { computed } from "vue";
import RatingWidgetEditor from "@/arches_component_lab/widgets/RatingWidget/components/RatingWidgetEditor.vue";
import RatingWidgetViewer from "@/arches_component_lab/widgets/RatingWidget/components/RatingWidgetViewer.vue";
import { EDIT, VIEW } from "@/arches_component_lab/widgets/constants.ts";
import { buildNumberAliasedNodeData } from "@/arches_component_lab/datatypes/number/utils.ts";
import type { NumberAliasedNodeData } from "@/arches_component_lab/datatypes/number/types.ts";
import type { RatingWidgetProps } from "@/arches_component_lab/widgets/RatingWidget/types.ts";
const { aliasedNodeData, value } = defineProps([
"mode",
"nodeAlias",
"graphSlug",
"cardXNodeXWidgetData",
"aliasedNodeData",
"value",
]) as RatingWidgetProps;
const emit = defineEmits([
"update:value",
"update:aliasedNodeData",
"initialized",
]) as {
(event: "update:value", updatedValue: number | null): void;
(event: "update:aliasedNodeData", updatedValue: NumberAliasedNodeData): void;
(event: "initialized", updatedValue: NumberAliasedNodeData): void;
};
const resolvedAliasedNodeData = computed(
() => aliasedNodeData ?? buildNumberAliasedNodeData(value ?? null),
);
function onUpdateAliasedNodeData(updated: NumberAliasedNodeData) {
emit("update:aliasedNodeData", updated);
emit("update:value", updated.node_value);
}
</script>
<template>
<RatingWidgetEditor
v-if="mode === EDIT"
:card-x-node-x-widget-data="cardXNodeXWidgetData"
:aliased-node-data="resolvedAliasedNodeData"
@update:aliased-node-data="onUpdateAliasedNodeData"
@initialized="emit('initialized', $event)"
/>
<RatingWidgetViewer
v-else-if="mode === VIEW"
:aliased-node-data="resolvedAliasedNodeData"
@initialized="emit('initialized', $event)"
/>
</template>
- Add the editor and viewer. Both emit
initializedon mount:
<!-- components/RatingWidgetEditor.vue -->
<script setup lang="ts">
import { onMounted } from "vue";
import Rating from "primevue/rating";
import { buildNumberAliasedNodeData } from "@/arches_component_lab/datatypes/number/utils.ts";
import type { NumberAliasedNodeData } from "@/arches_component_lab/datatypes/number/types.ts";
const { aliasedNodeData } = defineProps(["aliasedNodeData"]) as {
aliasedNodeData: NumberAliasedNodeData | null;
};
const emit = defineEmits(["update:aliasedNodeData", "initialized"]) as {
(event: "update:aliasedNodeData", updatedValue: NumberAliasedNodeData): void;
(event: "initialized", updatedValue: NumberAliasedNodeData): void;
};
onMounted(() => {
emit("initialized", aliasedNodeData ?? buildNumberAliasedNodeData(null));
});
function onUpdateModelValue(updatedValue: number | null) {
emit("update:aliasedNodeData", buildNumberAliasedNodeData(updatedValue));
}
</script>
<template>
<Rating
:model-value="aliasedNodeData?.node_value ?? null"
@update:model-value="onUpdateModelValue($event)"
/>
</template>
<!-- components/RatingWidgetViewer.vue -->
<script setup lang="ts">
import { onMounted } from "vue";
import type { NumberAliasedNodeData } from "@/arches_component_lab/datatypes/number/types.ts";
const { aliasedNodeData } = defineProps(["aliasedNodeData"]) as {
aliasedNodeData: NumberAliasedNodeData;
};
const emit = defineEmits(["initialized"]) as {
(event: "initialized", updatedValue: NumberAliasedNodeData): void;
};
onMounted(() => emit("initialized", aliasedNodeData));
</script>
<template>
<div>{{ aliasedNodeData?.display_value }}</div>
</template>
-
Reuse an existing datatype module (
@/arches_component_lab/datatypes— string, number, boolean, concept, domain, etc.) for the value shape, as above, or add a new one following the sametypes.ts+build<Datatype>AliasedNodeDatautils.tspattern. -
If contributing to Arches Component Lab, export it from
widgets/index.ts:
export { default as RatingWidget } from "@/arches_component_lab/widgets/RatingWidget/RatingWidget.vue";
export type { RatingWidgetProps } from "@/arches_component_lab/widgets/RatingWidget/types.ts";
- Register it — see Extending Arches Component Lab.
Extending Arches Component Lab
Arches Component Lab uses the WidgetMapping model to map widgets to their Vue components. To check for missing mappings:
python manage.py widget check_mappings
To add a mapping:
python manage.py widget add_mapping -wn language-select -cp arches_component_lab/widgets/LanguageSelectWidget/LanguageSelectWidget.vue
Metadata
Release files for arches-component-lab 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| arches_component_lab-0.1.0.tar.gz | 317.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| arches_component_lab-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 537.3 kB
Release files / arches_component_lab-0.1.0.tar.gz
| Download URL | arches_component_lab-0.1.0.tar.gz |
|---|---|
| Size | 317.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f31341ea85039dd402f189ebaafe8d4915c059592d30302644954d5be5626919
|
|
BLAKE2b-256 checksum How to use checksums |
443b91edcddc2abcbe07f92705669beaf519e230c258e482b9082363271043be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.5
|
Release files / arches_component_lab-0.1.0-py3-none-any.whl
| Download URL | arches_component_lab-0.1.0-py3-none-any.whl |
|---|---|
| Size | 219.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
796d416aa232f3b64c30f505ea93e7a39a945937e8fb825c9d4f03fee9c48e07
|
|
BLAKE2b-256 checksum How to use checksums |
95773333a1315ab3c02051bca6438e887f012c1bc836a1734819622f0abfdbab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.5
|