From 0ccb0bc4e34e9f1ad075f3488b82731cf726830d Mon Sep 17 00:00:00 2001 From: pkong-ds Date: Mon, 2 Sep 2024 13:50:44 +0800 Subject: [PATCH 1/4] Add types to algolia generate model/brand items --- src/scripts/algolia/generate.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/scripts/algolia/generate.ts b/src/scripts/algolia/generate.ts index 2a7849b..eaef09d 100644 --- a/src/scripts/algolia/generate.ts +++ b/src/scripts/algolia/generate.ts @@ -2,7 +2,21 @@ import { DeviceManager } from "../deviceManager"; import { BrandValue, ModelValue } from "../model"; import { BrandEnum, ModelEnum } from "../parse"; -export const getModelItems = (deviceManager: DeviceManager) => +export interface SearchBarModelItem { + id: ModelEnum; + name: string; + pathname: string; +} + +export interface SearchBarBrandItem { + id: BrandEnum; + name: string; + pathname: string; +} + +export const getModelItems = ( + deviceManager: DeviceManager, +): SearchBarModelItem[] => Object.keys(deviceManager.allDeviceModels) .map((modelKey) => { const _modelKey: ModelEnum = ModelEnum.parse(modelKey); @@ -20,7 +34,9 @@ export const getModelItems = (deviceManager: DeviceManager) => }; }); -export const getBrandItems = (deviceManager: DeviceManager) => +export const getBrandItems = ( + deviceManager: DeviceManager, +): SearchBarBrandItem[] => Object.keys(deviceManager.allBrands) .map((brandKey) => { const _brandKey: BrandEnum = BrandEnum.parse(brandKey); From f6c665fd1c210cb3515d60548431f55e2f8fb404 Mon Sep 17 00:00:00 2001 From: pkong-ds Date: Mon, 2 Sep 2024 14:36:20 +0800 Subject: [PATCH 2/4] Extract devicesearch as separate component --- .../DeviceSearch/DeviceSearch.astro | 31 +++ .../DeviceSearch}/search.css | 43 +++- .../DeviceSearch/search.js} | 64 +++--- src/layouts/BaseLayout/BaseLayout.astro | 9 +- src/pages/index.astro | 12 +- src/pages/type/[type].astro | 15 +- src/pages/type/_device.js | 193 +----------------- src/pages/type/index.css | 40 ---- src/styles/home.css | 2 - 9 files changed, 117 insertions(+), 292 deletions(-) create mode 100644 src/components/DeviceSearch/DeviceSearch.astro rename src/{styles => components/DeviceSearch}/search.css (77%) rename src/{pages/_home.js => components/DeviceSearch/search.js} (87%) diff --git a/src/components/DeviceSearch/DeviceSearch.astro b/src/components/DeviceSearch/DeviceSearch.astro new file mode 100644 index 0000000..6ec2d10 --- /dev/null +++ b/src/components/DeviceSearch/DeviceSearch.astro @@ -0,0 +1,31 @@ +--- +import "./search.css"; + +import { DEVICE_MANAGER } from "../../scripts/deviceManager"; +import { getModelItems, getBrandItems } from "../../scripts/algolia/generate"; +const modelItems = getModelItems(DEVICE_MANAGER); +const brandItems = getBrandItems(DEVICE_MANAGER); + +interface Props { + id: string; +} + +const { id } = Astro.props; +--- + + + +<> +
+ + diff --git a/src/styles/search.css b/src/components/DeviceSearch/search.css similarity index 77% rename from src/styles/search.css rename to src/components/DeviceSearch/search.css index aca5d49..8766a05 100644 --- a/src/styles/search.css +++ b/src/components/DeviceSearch/search.css @@ -130,10 +130,47 @@ height: 100%; } - /* .aa-InputWrapperPrefix > .aa-DetachedCancelButton { - height: 100%; - } */ .aa-InputWrapperSuffix { width: 48px; } } + +/* header variant styles */ + +#device-list__header__autocomplete { + max-width: 38%; /* 543 / 1440 */ + flex: 1 1 0%; + margin: 0; + height: 56px; + background: var(--white); +} + +@media (width >= 992px) { + #device-list__header__autocomplete { + margin: 0 0 0 144px; /* to prevent search bar overlapping with mockuphone logo */ + } +} + +@media (width >= 1300px) { + #device-list__header__autocomplete { + margin: 0; + } +} + +.search-device { + display: flex; + padding: 40px 20px 0; +} + +@media (width >= 992px) { + .search-device { + display: none; + } +} + +#device-list__page__autocomplete { + flex: 1 1 0%; + margin: 0; + height: 56px; + background: var(--white); +} diff --git a/src/pages/_home.js b/src/components/DeviceSearch/search.js similarity index 87% rename from src/pages/_home.js rename to src/components/DeviceSearch/search.js index 37b5559..5a3bee8 100644 --- a/src/pages/_home.js +++ b/src/components/DeviceSearch/search.js @@ -2,39 +2,22 @@ import * as autocompletePluginRecentSearchesPkg from "@algolia/autocomplete-plug const { createLocalStorageRecentSearchesPlugin } = autocompletePluginRecentSearchesPkg; import * as autocompleteJsPkg from "@algolia/autocomplete-js"; -const { autocomplete } = autocompleteJsPkg; -const NUM_DEFAULT_MODEL_ITEMS_TO_DISPLAY = 0; -const NUM_DEFAULT_BRAND_ITEMS_TO_DISPLAY = 0; -const MAX_SEARCH_HISTORY_ITEM = 5; const ALGOLIA_SEARCH_HISTORY_KEY = "brandModelSearch"; const LOCAL_STORAGE_KEY = `AUTOCOMPLETE_RECENT_SEARCHES:${ALGOLIA_SEARCH_HISTORY_KEY}`; -class RootViewModel { - searchText = ""; - _modelItems; - _brandItems; - - constructor(modelItems, brandItems) { - mobx.makeObservable(this, { - searchText: mobx.observable, - shouldShowSearchClear: mobx.computed, - }); - this._modelItems = modelItems; - this._brandItems = brandItems; - } +const { autocomplete } = autocompleteJsPkg; - get shouldShowSearchClear() { - return this.searchText !== ""; - } -} +const NUM_DEFAULT_MODEL_ITEMS_TO_DISPLAY = 0; +const NUM_DEFAULT_BRAND_ITEMS_TO_DISPLAY = 0; +const MAX_SEARCH_HISTORY_ITEM = 5; function isArray(obj) { return Object.prototype.toString.call(obj) === "[object Array]"; } function appendToLocalStorageRecentSearches(item, type) { - const existingStr = localStorage.getItem(LOCAL_STORAGE_KEY); + const existingStr = localStorage.getItem(LOCAL_STORAGE_KEY) ?? ""; const existing = JSON.parse(existingStr); const newHistoryItem = { id: item.id, label: item.name, type }; @@ -73,7 +56,15 @@ function moveOldHistoryToTop(oldHistoryItem) { localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newHistory)); } -function initializeAutocomplete(viewModel) { +function injectVariables() { + return { + modelItems: window.modelItems, + brandItems: window.brandItems, + containerIds: window.containerIds, + }; +} + +function initializeAutocomplete(containerId) { const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({ key: ALGOLIA_SEARCH_HISTORY_KEY, MAX_SEARCH_HISTORY_ITEM, @@ -81,15 +72,15 @@ function initializeAutocomplete(viewModel) { return { ...source, onSelect({ item }) { - const { id, label } = item; + const { id: itemId, label } = item; const type = item.type ?? ""; moveOldHistoryToTop(item); // move most recent to top switch (type) { case "model": - window.location.href = `/model/${id}`; + window.location.href = `/model/${itemId}`; break; case "brand": - window.location.href = `/type/all/?brand=${id}`; + window.location.href = `/type/all/?brand=${itemId}`; break; default: window.location.href = `/type/all/?query=${label}`; @@ -98,12 +89,8 @@ function initializeAutocomplete(viewModel) { }; }, }); - - const modelItems = viewModel._modelItems; - const brandItems = viewModel._brandItems; - autocomplete({ - container: "#homepage-autocomplete", + container: `#${containerId}`, openOnFocus: true, plugins: [recentSearchesPlugin], placeholder: "Search Device", @@ -167,6 +154,18 @@ function initializeAutocomplete(viewModel) { window.location.href = `${window.location.origin}/type/all/?query=${state.query}`; }, }); +} + +function initialize() { + const { modelItems, brandItems, containerIds } = injectVariables(); + console.log(modelItems, brandItems, containerIds); + containerIds.forEach((containerId) => + console.log(document.querySelector(`#${containerId}`)), + ); + + containerIds.forEach((containerId) => { + initializeAutocomplete(containerId); + }); tippy(".aa-ClearButton", { content: "Clear", @@ -208,8 +207,7 @@ function ready(fn) { } function main() { - const viewModel = new RootViewModel(window.modelItems, window.brandItems); - initializeAutocomplete(viewModel); + initialize(); } ready(main); diff --git a/src/layouts/BaseLayout/BaseLayout.astro b/src/layouts/BaseLayout/BaseLayout.astro index 95e35a4..b75d74a 100644 --- a/src/layouts/BaseLayout/BaseLayout.astro +++ b/src/layouts/BaseLayout/BaseLayout.astro @@ -5,6 +5,7 @@ import "bootstrap/dist/css/bootstrap.min.css"; import "/src/styles/main.css"; import "/src/styles/tailwind.css"; import { Image } from "@astrojs/image/components"; +import DeviceSearch from "../../components/DeviceSearch/DeviceSearch.astro"; const title = "MockUPhone"; const description = @@ -13,6 +14,12 @@ const social_url = "home_url"; const social_icon = `${ import.meta.env.PUBLIC_BASE_URL }/images/mockuphone-logo.png`; + +interface Props { + shouldRenderSearchBar?: boolean; + isHeaderTransparent?: boolean; + mainContainerClass?: string; +} const { shouldRenderSearchBar, isHeaderTransparent = false, @@ -134,7 +141,7 @@ const { { !!shouldRenderSearchBar ? (
-
+
) : undefined } diff --git a/src/pages/index.astro b/src/pages/index.astro index c8f0ab8..459c91c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -3,21 +3,13 @@ import "/src/styles/home.css"; import BaseLayout from "../layouts/BaseLayout/BaseLayout.astro"; import { Image } from "@astrojs/image/components"; -import { DEVICE_MANAGER } from "../scripts/deviceManager"; -import { getModelItems, getBrandItems } from "../scripts/algolia/generate"; -const modelItems = getModelItems(DEVICE_MANAGER); -const brandItems = getBrandItems(DEVICE_MANAGER); +import DeviceSearch from "../components/DeviceSearch/DeviceSearch.astro"; --- - -

@@ -27,7 +19,7 @@ const brandItems = getBrandItems(DEVICE_MANAGER); Wrap your design in mobile devices in a few clicks!

-
+