diff --git a/config/stache.php b/config/stache.php
index 6cca12baba0..47ea8110840 100644
--- a/config/stache.php
+++ b/config/stache.php
@@ -76,6 +76,11 @@
'directory' => base_path('content/trees/navigation'),
],
+ 'taxonomy-trees' => [
+ 'class' => Stores\TaxonomyTreeStore::class,
+ 'directory' => base_path('content/trees/taxonomies'),
+ ],
+
'globals' => [
'class' => Stores\GlobalsStore::class,
'directory' => base_path('content/globals'),
diff --git a/lang/en/fieldtypes.php b/lang/en/fieldtypes.php
index fa5ec422d0d..a8a43a268f3 100644
--- a/lang/en/fieldtypes.php
+++ b/lang/en/fieldtypes.php
@@ -121,6 +121,7 @@
'integer.title' => 'Integer',
'link.config.collections' => 'Entries from these collections will be available. Leaving this empty will make entries from routable collections available.',
'link.config.container' => 'Choose which asset container to use for this field.',
+ 'link.config.taxonomies' => 'Terms from these taxonomies will be available. Leaving this empty will make terms from routable taxonomies available.',
'link.config.default_option' => 'The default Link type option, if available for the fieldtype\'s configuration.',
'link.title' => 'Link',
'list.config.add_row' => 'Customize the label of the "Add Item" button.',
diff --git a/lang/en/messages.php b/lang/en/messages.php
index f6339ba62df..8437d32d95a 100644
--- a/lang/en/messages.php
+++ b/lang/en/messages.php
@@ -254,8 +254,12 @@
'sync_term_field_confirmation_text' => 'Are you sure? This field\'s value will be replaced by the value in the originating term.',
'taxonomies_blueprints_instructions' => 'Terms in this taxonomy may use any of these blueprints.',
'taxonomies_collections_instructions' => 'The collections that use this taxonomy.',
+ 'taxonomies_max_depth_instructions' => 'Set the max term nesting level. A depth of 1 allows reordering without nesting, keeping URLs flat.',
+ 'taxonomies_orderable_instructions' => 'Enable manual ordering and hierarchy (parent/child terms) via drag & drop. Nested terms get nested URLs.',
'taxonomies_preview_target_refresh_instructions' => 'Automatically refresh the preview while editing. Disabling this will use postMessage.',
'taxonomies_preview_targets_instructions' => 'The URLs to be viewable within Live Preview. Learn more in the [documentation](https://statamic.dev/live-preview#preview-targets).',
+ 'taxonomies_routes_instructions' => 'The route controls the terms URL pattern. Learn more in the [documentation](https://statamic.dev/taxonomies#routing).',
+ 'taxonomies_route_instructions' => 'The URL pattern for term pages. Include `{slug}` (and `{parent_uri}` when nested).',
'taxonomy_configure_handle_instructions' => 'Used to reference this taxonomy on the frontend. This cannot be easily changed later.',
'taxonomy_configure_intro' => 'A taxonomy is a system of classifying data around a set of unique characteristics, such as categories, tags, or colors.',
'taxonomy_configure_layout_instructions' => 'Set this taxonomy\'s default layout. Terms can override this setting with a `layout` field.',
@@ -265,6 +269,8 @@
'taxonomy_next_steps_blueprints_description' => 'Manage the blueprints and fields available for this taxonomy.',
'taxonomy_next_steps_configure_description' => 'Configure names, associate collections, define blueprints, and more.',
'taxonomy_next_steps_create_term_description' => 'Create the first term or stub out a handful of placeholder terms—it\'s up to you.',
+ 'taxonomy_next_steps_scaffold_description' => 'Generate index and detail views from the taxonomy name at the click of a button.',
+ 'taxonomy_scaffold_instructions' => 'Select and configure views to generate. Any existing files will be preserved.',
'theme_share_instructions' => 'Publish this theme through your statamic.com account to make it available to others.',
'try_again_in_seconds' => '{0,1}Try again now.|Try again in :count seconds.',
'try_again_in_minutes' => 'Try again in a minute.|Try again in :count minutes.',
diff --git a/lang/en/validation.php b/lang/en/validation.php
index 9e46026ccab..c24a2124122 100644
--- a/lang/en/validation.php
+++ b/lang/en/validation.php
@@ -177,9 +177,12 @@
'options_require_keys' => 'All options must have keys.',
'origin_cannot_be_disabled' => 'Cannot select a disabled origin.',
'parent_cannot_be_itself' => 'Cannot be its own parent.',
+ 'parent_cannot_be_descendant' => 'Cannot be nested under one of its descendants.',
'parent_causes_root_children' => 'This would cause the root page to have children.',
'parent_exceeds_max_depth' => 'This would exceed the maximum depth.',
'reserved' => 'This is a reserved word.',
+ 'term_path_exceeds_max_depth' => 'The path :path exceeds the maximum depth of :max.',
+ 'taxonomy_route_requires_slug' => 'The route must include {slug}.',
'reserved_field_handle' => 'Field with a handle of :handle is a reserved word.',
'unique_entry_value' => 'This value has already been taken.',
'unique_form_handle' => 'This value has already been taken.',
diff --git a/resources/js/components/fieldtypes/relationship/RelationshipFieldtype.vue b/resources/js/components/fieldtypes/relationship/RelationshipFieldtype.vue
index 843eb55c4ac..ee1367318c7 100644
--- a/resources/js/components/fieldtypes/relationship/RelationshipFieldtype.vue
+++ b/resources/js/components/fieldtypes/relationship/RelationshipFieldtype.vue
@@ -3,7 +3,7 @@
ref="input"
:name="name"
:value="value"
- :mode="config.mode"
+ :mode="mode"
:can-edit="canEdit"
:config="config"
:can-create="canCreate"
@@ -48,6 +48,10 @@ export default {
},
computed: {
+ mode() {
+ return this.meta?.mode || this.config.mode;
+ },
+
maxItems() {
return this.config.max_items || Infinity;
},
diff --git a/resources/js/components/inputs/relationship/Item.vue b/resources/js/components/inputs/relationship/Item.vue
index 707a589808a..468df69e403 100644
--- a/resources/js/components/inputs/relationship/Item.vue
+++ b/resources/js/components/inputs/relationship/Item.vue
@@ -2,27 +2,63 @@