diff --git a/frontend/src/assets/icons.generated.js b/frontend/src/assets/icons.generated.js
index 818bd318a..7dd13c0de 100644
--- a/frontend/src/assets/icons.generated.js
+++ b/frontend/src/assets/icons.generated.js
@@ -5,7 +5,7 @@
never waits on (or depends on) the icon service. Regenerate with `npm run icons` after adding or
removing an icon; `check-icons.mjs` fails the build if this drifts.
- 264 icons.
+ 263 icons.
*/
export const BUNDLED_ICONS = {
"la:angle-double-right": {"body":"","width":32,"height":32},
@@ -153,7 +153,6 @@ export const BUNDLED_ICONS = {
"mdi:account-group": {"body":"","width":24,"height":24},
"mdi:alert": {"body":"","width":24,"height":24},
"mdi:alert-box": {"body":"","width":24,"height":24},
- "mdi:alpha-t-box-outline": {"body":"","width":24,"height":24},
"mdi:arrow-right": {"body":"","width":24,"height":24},
"mdi:arrow-vertical-lock": {"body":"","width":24,"height":24},
"mdi:basketball": {"body":"","width":24,"height":24},
diff --git a/frontend/src/components/HeaderActionsMenu.vue b/frontend/src/components/HeaderActionsMenu.vue
new file mode 100644
index 000000000..d0bd94760
--- /dev/null
+++ b/frontend/src/components/HeaderActionsMenu.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/components/HeaderNav.vue b/frontend/src/components/HeaderNav.vue
index ad5181f4c..c80fb1195 100644
--- a/frontend/src/components/HeaderNav.vue
+++ b/frontend/src/components/HeaderNav.vue
@@ -8,99 +8,141 @@
-
+
{{ siteStore.title }}
-
+
+
-
- Create New Page
-
-
-
- File Manager
-
-
- {{ t('inbox.title') }}
-
-
- {{ t('common.header.admin') }}
-
+
+
+
+
+
+
+ Create New Page
+
+
+
+
+ File Manager
+
+
+ {{ t('inbox.title') }}
+
+
+ {{ t('common.header.admin') }}
+
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/HeaderSearch.vue b/frontend/src/components/HeaderSearch.vue
index 948d2a133..5585423b0 100644
--- a/frontend/src/components/HeaderSearch.vue
+++ b/frontend/src/components/HeaderSearch.vue
@@ -1,5 +1,11 @@
-
+
+
@@ -105,6 +129,18 @@ import { useSiteStore } from '@/stores/site'
import { orderBy } from 'es-toolkit/array'
+// PROPS
+
+const props = defineProps({
+ /**
+ * Render as a row of its own rather than inline in the header bar. What the phone header opens;
+ * see `HeaderNav`.
+ */
+ row: {
+ type: Boolean,
+ default: false
+ }
+})
// STORES
@@ -190,6 +226,18 @@ function checkSearchFocus(ev) {
}
}
+/**
+ * Put the caret in the field.
+ *
+ * Exposed because in `row` form the field is not focused by being mounted: focusing it is what draws
+ * the panel below it, and a panel appearing mid-slide is layout and a `backdrop-filter` blur landing
+ * in the middle of an animation. `HeaderNav` owns that transition, so it calls this when the slide has
+ * finished -- see its `@after-enter`.
+ */
+function focus() {
+ searchField.value?.focus()
+}
+
function clearSearch() {
siteStore.search = ''
searchField.value.focus()
@@ -217,6 +265,8 @@ onBeforeUnmount(() => {
window.removeEventListener('keydown', handleKeyPress)
}
})
+
+defineExpose({ focus })