diff --git a/src/components/VContentSwitcher/VContentSwitcherButton.vue b/src/components/VContentSwitcher/VContentSwitcherButton.vue
new file mode 100644
index 0000000000..09042091e0
--- /dev/null
+++ b/src/components/VContentSwitcher/VContentSwitcherButton.vue
@@ -0,0 +1,85 @@
+
+
+
+ {{
+ buttonLabel
+ }}
+
+
+
+
diff --git a/src/components/VContentSwitcher/VContentSwitcherPopover.vue b/src/components/VContentSwitcher/VContentSwitcherPopover.vue
new file mode 100644
index 0000000000..8acec2b724
--- /dev/null
+++ b/src/components/VContentSwitcher/VContentSwitcherPopover.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/VContentSwitcher/VContentTypes.vue b/src/components/VContentSwitcher/VContentTypes.vue
new file mode 100644
index 0000000000..053480f536
--- /dev/null
+++ b/src/components/VContentSwitcher/VContentTypes.vue
@@ -0,0 +1,59 @@
+
+
+
+
+ {{
+ $t(`search-type.${item}`)
+ }}
+
+
+
+
diff --git a/src/composables/use-content-type.js b/src/composables/use-content-type.js
new file mode 100644
index 0000000000..20cc551b6a
--- /dev/null
+++ b/src/composables/use-content-type.js
@@ -0,0 +1,34 @@
+import { computed, ref, useContext } from '@nuxtjs/composition-api'
+import { supportedContentTypes } from '~/constants/media'
+import { SEARCH } from '~/constants/store-modules'
+import { UPDATE_QUERY } from '~/constants/action-types'
+import allIcon from '~/assets/icons/all-content.svg'
+import audioIcon from '~/assets/icons/audio-content.svg'
+import imageIcon from '~/assets/icons/image-content.svg'
+
+const icons = {
+ all: allIcon,
+ audio: audioIcon,
+ image: imageIcon,
+}
+export default function useContentType() {
+ const { store } = useContext()
+
+ const contentTypes = [...supportedContentTypes]
+
+ const activeType = computed(() => store.state.search.searchType)
+ const previousContentType = ref(activeType.value)
+ const setActiveType = async (contentType) => {
+ if (previousContentType.value === contentType) return
+ await store.dispatch(`${SEARCH}/${UPDATE_QUERY}`, {
+ searchType: contentType,
+ })
+ previousContentType.value = contentType
+ }
+ return {
+ setActiveType,
+ activeType,
+ types: contentTypes,
+ icons,
+ }
+}
diff --git a/src/constants/media.js b/src/constants/media.js
index 6a7b7e570f..852d89deec 100644
--- a/src/constants/media.js
+++ b/src/constants/media.js
@@ -5,11 +5,15 @@ export const ALL_MEDIA = 'all'
/** @typedef {typeof AUDIO | typeof IMAGE | typeof VIDEO | typeof ALL_MEDIA} MediaType */
-// Media types
-/** @type {MediaType[]} */
-export const mediaTypes = [AUDIO, IMAGE]
-// Media types which support custom filters
-/** @type {MediaType[]} */
-export const supportedMediaTypes = [AUDIO, IMAGE, VIDEO]
-/** @type {MediaType[]} */
-export const allMediaTypes = [ALL_MEDIA, IMAGE, AUDIO, VIDEO]
+/**
+ * Media types that the API supports.
+ * These types also support custom filters.
+ * @type {MediaType[]}
+ */
+export const supportedMediaTypes = [IMAGE, AUDIO]
+
+/**
+ * The types of content that users can search. `All` is also an option here.
+ * @type {MediaType[]}
+ */
+export const supportedContentTypes = [ALL_MEDIA, IMAGE, AUDIO]
diff --git a/src/locales/en.json b/src/locales/en.json
index da716e56ea..55de052caf 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -690,5 +690,13 @@
"interpunct": "•",
"modal": {
"close": "Close"
+ },
+ "search-type": {
+ "image": "Images",
+ "audio": "Audio",
+ "all": "All content",
+ "video": "Videos",
+ "label": "Type of content to search",
+ "heading": "Content types"
}
}
diff --git a/src/locales/po-files/openverse.pot b/src/locales/po-files/openverse.pot
index 0251efa53f..fcfe6f228c 100644
--- a/src/locales/po-files/openverse.pot
+++ b/src/locales/po-files/openverse.pot
@@ -4,7 +4,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Openverse \n"
"Report-Msgid-Bugs-To: https://github.com/wordpress/openverse/issues \n"
-"POT-Creation-Date: 2022-01-19T10:51:29+00:00\n"
+"POT-Creation-Date: 2022-01-19T15:13:27+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -18,6 +18,31 @@ msgctxt "interpunct"
msgid "•"
msgstr ""
+msgctxt "search-type.image"
+msgid "Images"
+msgstr ""
+
+msgctxt "search-type.audio"
+msgid "Audio"
+msgstr ""
+
+msgctxt "search-type.all"
+msgid "All content"
+msgstr ""
+
+msgctxt "search-type.video"
+msgid "Videos"
+msgstr ""
+
+#: src/components/VContentSwitcher/VContentSwitcherPopover.vue:5
+msgctxt "search-type.label"
+msgid "Type of content to search"
+msgstr ""
+
+msgctxt "search-type.heading"
+msgid "Content types"
+msgstr ""
+
#: src/components/VModal/VModalContent.vue:25
msgctxt "modal.close"
msgid "Close"
@@ -2135,12 +2160,12 @@ msgid "I want something I can"
msgstr ""
#. Do not translate words between ### ###.
-#: src/pages/index.vue:18
+#: src/pages/index.vue:26
msgctxt "hero.disclaimer.content"
msgid "All our content is under a ###license### or in the public domain."
msgstr ""
-#: src/pages/index.vue:27
+#: src/pages/index.vue:35
msgctxt "hero.disclaimer.license"
msgid "Creative Commons license"
msgstr ""
diff --git a/src/pages/index.vue b/src/pages/index.vue
index da6d3c61ea..0b3b79a117 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -13,7 +13,15 @@
{{ $t('hero.subtitle') }}
-
+
+
+
@@ -140,8 +154,33 @@ const HomePage = {
}
})
+ const isMounted = ref(false)
+ onMounted(() => {
+ isMounted.value = true
+ })
+ onBeforeUnmount(() => {
+ isMounted.value = false
+ })
+
+ const isMinScreenMd = isMinScreen('md', { shouldPassInSSR: true })
+
+ const contentSwitcher = ref(null)
+ const contentType = ref(ALL_MEDIA)
+ const setContentType = (type) => {
+ contentType.value = type
+ contentSwitcher.value?.closeMenu()
+ }
+
return {
featuredSearch,
+
+ isMounted,
+
+ isMinScreenMd,
+
+ contentSwitcher,
+ contentType,
+ setContentType,
}
},
}