diff --git a/README.md b/README.md index 8acc99a..d253fe3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Halo 2.0 的通用搜索组件插件。 +![Preview](./images/preview.png) + ## 使用方式 1. 下载,目前提供以下两个下载方式: @@ -68,46 +70,54 @@ halo: 目前已提供的 CSS 变量: -| 变量名 | 描述 | -| --------------------------------------------------- | ------------------ | -| `--halo-search-widget-base-font-size` | 基础字体大小 | -| `--halo-search-widget-base-border-radius` | 基础元素的圆角 | -| `--halo-search-widget-base-font-family` | 基础字体族 | -| `--halo-search-widget-color-modal-layer` | 模态层颜色 | -| `--halo-search-widget-color-modal-content-bg` | 模态内容背景颜色 | -| `--halo-search-widget-color-form-input-bg` | 表单输入背景颜色 | -| `--halo-search-widget-color-form-input` | 表单输入文字颜色 | -| `--halo-search-widget-color-form-input-placeholder` | 表单输入占位符颜色 | -| `--halo-search-widget-color-form-divider` | 表单分隔线颜色 | -| `--halo-search-widget-color-result-empty` | 无结果提示颜色 | -| `--halo-search-widget-color-result-item-bg` | 结果项背景颜色 | -| `--halo-search-widget-color-result-item-hover-bg` | 结果项悬停背景颜色 | -| `--halo-search-widget-color-result-item-title` | 结果项标题颜色 | -| `--halo-search-widget-color-result-item-content` | 结果项内容颜色 | -| `--halo-search-widget-color-command-kbd-item` | 命令键盘项颜色 | -| `--halo-search-widget-color-command-kbd-border` | 命令键盘边框颜色 | +| 变量名 | 描述 | 备注 | +|-----------------------------------------------------|------------------|----------------------------------------------------------------------------------| +| `--halo-search-widget-base-font-size` | 基础字体大小 | -- | +| `--halo-search-widget-base-font-family` | 字体 | -- | +| `--halo-search-widget-base-rounded` | 边框圆角 | -- | +| `--halo-search-widget-base-bg-color` | 基础背景色 | -- | +| `--halo-search-widget-primary-color` | 主色 | -- | +| `--halo-search-widget-muted-color` | 辅助色 | -- | +| `--halo-search-widget-content-color` | 文本色 | -- | +| `--halo-search-widget-modal-bg-color` | 模态框背景色 | -- | +| `--halo-search-widget-modal-layer-color` | 模态框遮挡层背景色 | -- | +| `--halo-search-widget-hit-bg-color` | 搜索结果条目背景色 | -- | +| `--halo-search-widget-divider-color` | 分割线颜色 | -- | +| `--halo-search-widget-kbd-border-color` | 快捷键图标边框颜色 | -- | +| `--halo-search-widget-kbd-shadow` | 快捷键图标阴影 | -- | +| `--halo-search-widget-base-border-radius` | 基础元素的圆角 | 已过时,将在未来的版本移除,后续使用 `--halo-search-widget-base-rounded` 代替 | +| `--halo-search-widget-color-modal-layer` | 模态层颜色 | 已过时,将在未来的版本移除,后续使用 `--halo-search-widget-modal-layer-color` 代替 | +| `--halo-search-widget-color-modal-content-bg` | 模态内容背景颜色 | 已过时,将在未来的版本移除,后续使用 `--halo-search-widget-modal-bg-color` 代替 | +| `--halo-search-widget-color-form-input-bg` | 表单输入背景颜色 | 已过时,将在未来的版本移除 | +| `--halo-search-widget-color-form-input` | 表单输入文字颜色 | 已过时,将在未来的版本移除 | +| `--halo-search-widget-color-form-input-placeholder` | 表单输入占位符颜色 | 已过时,将在未来的版本移除 | +| `--halo-search-widget-color-form-divider` | 表单分隔线颜色 | 已过时,将在未来的版本移除,后续使用 `--halo-search-widget-divider-color` 代替 | +| `--halo-search-widget-color-result-empty` | 无结果提示颜色 | 已过时,将在未来的版本移除 | +| `--halo-search-widget-color-result-item-bg` | 结果项背景颜色 | 已过时,将在未来的版本移除,后续使用 `--halo-search-widget-hit-bg-color` 代替 | +| `--halo-search-widget-color-result-item-hover-bg` | 结果项悬停背景颜色 | 已过时,将在未来的版本移除 | +| `--halo-search-widget-color-result-item-title` | 结果项标题颜色 | 已过时,将在未来的版本移除 | +| `--halo-search-widget-color-result-item-content` | 结果项内容颜色 | 已过时,将在未来的版本移除 | +| `--halo-search-widget-color-command-kbd-item` | 命令键盘项颜色 | 已过时,将在未来的版本移除 | +| `--halo-search-widget-color-command-kbd-border` | 命令键盘边框颜色 | 已过时,将在未来的版本移除,后续使用 `--halo-search-widget-kbd-border-color` 代替 |
点击查看 CSS 代码模板 ```css -:root { - --halo-search-widget-base-font-size: ; - --halo-search-widget-base-border-radius: ; - --halo-search-widget-base-font-family: ; - --halo-search-widget-color-modal-layer: ; - --halo-search-widget-color-modal-content-bg: ; - --halo-search-widget-color-form-input-bg: ; - --halo-search-widget-color-form-input: ; - --halo-search-widget-color-form-input-placeholder: ; - --halo-search-widget-color-form-divider: ; - --halo-search-widget-color-result-empty: ; - --halo-search-widget-color-result-item-bg: ; - --halo-search-widget-color-result-item-hover-bg: ; - --halo-search-widget-color-result-item-title: ; - --halo-search-widget-color-result-item-content: ; - --halo-search-widget-color-command-kbd-item: ; - --halo-search-widget-color-command-kbd-border: ; +:root{ + --halo-search-widget-base-font-size: ; + --halo-search-widget-base-font-family: ; + --halo-search-widget-base-rounded: ; + --halo-search-widget-base-bg-color: ; + --halo-search-widget-primary-color: ; + --halo-search-widget-muted-color: ; + --halo-search-widget-content-color: ; + --halo-search-widget-modal-bg-color: ; + --halo-search-widget-modal-layer-color: ; + --halo-search-widget-hit-bg-color: ; + --halo-search-widget-divider-color: ; + --halo-search-widget-kbd-border-color: ; + --halo-search-widget-kbd-shadow: ; } ``` @@ -121,39 +131,34 @@ halo: ```css @media (prefers-color-scheme: dark) { - .color-scheme-auto { + .color-scheme-auto, + [data-color-scheme='auto'] { color-scheme: dark; - --halo-search-widget-color-modal-layer: rgba(0, 0, 0, 0.8); - --halo-search-widget-color-modal-content-bg: rgb(15 23 42); - --halo-search-widget-color-form-input: rgb(255, 255, 255); - --halo-search-widget-color-form-input-placeholder: rgb(148 163 184); - --halo-search-widget-color-form-input-bg: rgb(15 23 42); - --halo-search-widget-color-form-divider: rgb(30 41 59); - --halo-search-widget-color-result-item-bg: rgb(30 41 59); - --halo-search-widget-color-result-item-hover-bg: rgb(51 65 85); - --halo-search-widget-color-result-item-title: rgb(255 255 255); - --halo-search-widget-color-result-item-content: rgb(148 163 184); - --halo-search-widget-color-command-kbd-item: rgb(148 163 184); - --halo-search-widget-color-command-kbd-border: rgb(30 41 59); - --halo-search-widget-color-result-empty: rgb(148 163 184); + --halo-search-widget-muted-color: #cbd5e1; + --halo-search-widget-content-color: #f1f5f9; + --halo-search-widget-hit-bg-color: #090a11; + --halo-search-widget-modal-bg-color: #15172a; + --halo-search-widget-modal-layer-color: #000000cc; + --halo-search-widget-base-bg-color: #090a11; + --halo-search-widget-divider-color: #1e293b; + --halo-search-widget-kbd-border-color: #334155; + --halo-search-widget-kbd-shadow: 0px 2px 0px 0px #ffffff1a; } } -.color-scheme-dark { +.color-scheme-dark, +.dark, +[data-color-scheme='dark'] { color-scheme: dark; - --halo-search-widget-color-modal-layer: rgba(0, 0, 0, 0.8); - --halo-search-widget-color-modal-content-bg: rgb(15 23 42); - --halo-search-widget-color-form-input: rgb(255, 255, 255); - --halo-search-widget-color-form-input-placeholder: rgb(148 163 184); - --halo-search-widget-color-form-input-bg: rgb(15 23 42); - --halo-search-widget-color-form-divider: rgb(30 41 59); - --halo-search-widget-color-result-item-bg: rgb(30 41 59); - --halo-search-widget-color-result-item-hover-bg: rgb(51 65 85); - --halo-search-widget-color-result-item-title: rgb(255 255 255); - --halo-search-widget-color-result-item-content: rgb(148 163 184); - --halo-search-widget-color-command-kbd-item: rgb(148 163 184); - --halo-search-widget-color-command-kbd-border: rgb(30 41 59); - --halo-search-widget-color-result-empty: rgb(148 163 184); + --halo-search-widget-muted-color: #cbd5e1; + --halo-search-widget-content-color: #f1f5f9; + --halo-search-widget-hit-bg-color: #090a11; + --halo-search-widget-modal-bg-color: #15172a; + --halo-search-widget-modal-layer-color: #000000cc; + --halo-search-widget-base-bg-color: #090a11; + --halo-search-widget-divider-color: #1e293b; + --halo-search-widget-kbd-border-color: #334155; + --halo-search-widget-kbd-shadow: 0px 2px 0px 0px #ffffff1a; } ``` @@ -167,3 +172,7 @@ halo: 1. `auto`:自动模式,根据系统的暗黑模式自动切换。 2. `dark`:强制暗黑模式。 3. `light`:强制明亮模式。 + +## Credits + +- [Algolia DocSearch](https://github.com/algolia/docsearch): 搜索组件的 UI 设计灵感来源于 DocSearch。 diff --git a/build.gradle b/build.gradle index 2cafbb6..a75f60f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ plugins { id 'java' id "io.freefair.lombok" version "8.0.1" - id "com.github.node-gradle.node" version "5.0.0" - id "run.halo.plugin.devtools" version "0.0.7" + id "com.github.node-gradle.node" version "7.0.2" + id "run.halo.plugin.devtools" version "0.1.1" } group 'run.halo.search.widget' @@ -31,17 +31,20 @@ node { nodeProjectDir = file("${project.projectDir}") } -tasks.register('installDepsForUI', PnpmTask) { - args = ['install'] -} - tasks.register('buildFrontend', PnpmTask) { args = ['build:packages'] dependsOn('installDepsForUI') } -tasks.named('build').configure { - dependsOn('buildFrontend') +tasks.register('installDepsForUI', PnpmTask) { + args = ['install'] +} + +build { + // build frontend before build + tasks.named('compileJava').configure { + dependsOn('buildFrontend') + } } halo { diff --git a/images/preview.png b/images/preview.png new file mode 100644 index 0000000..1099ab8 Binary files /dev/null and b/images/preview.png differ diff --git a/package.json b/package.json index 136f1a4..ee95dd1 100644 --- a/package.json +++ b/package.json @@ -13,18 +13,18 @@ ], "scripts": { "build:packages": "pnpm --filter \"./packages/**\" build", - "example:dev": "pnpm --filter \"./packages/example\" dev", + "example:dev": "pnpm --filter \"./packages/search-widget\" dev", "release:packages": "pnpm --filter \"./packages/**\" release" }, "devDependencies": { "@halo-dev/api-client": "2.17.0", - "@rushstack/eslint-patch": "^1.5.1", - "@types/node": "^18.18.6", - "@typescript-eslint/eslint-plugin": "^6.9.0", - "@typescript-eslint/parser": "^6.9.0", - "eslint": "^8.52.0", + "@rushstack/eslint-patch": "^1.10.4", + "@types/node": "^18.19.43", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint": "^8.57.0", "prettier": "^2.8.8", - "typescript": "^5.2.2", - "vite": "^4.5.0" + "typescript": "^5.5.4", + "vite": "^5.3.5" } } diff --git a/packages/example/.gitignore b/packages/example/.gitignore deleted file mode 100644 index 38adffa..0000000 --- a/packages/example/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -.DS_Store -dist -dist-ssr -coverage -*.local - -/cypress/videos/ -/cypress/screenshots/ - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/packages/example/index.html b/packages/example/index.html deleted file mode 100644 index cf2a771..0000000 --- a/packages/example/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - Vite + Lit + TS - - - - - - - - - diff --git a/packages/example/package.json b/packages/example/package.json deleted file mode 100644 index 5a086fe..0000000 --- a/packages/example/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "scripts": { - "dev": "vite" - }, - "dependencies": { - "@halo-dev/search-widget": "workspace:*" - } -} diff --git a/packages/example/src/main.ts b/packages/example/src/main.ts deleted file mode 100644 index b2901ac..0000000 --- a/packages/example/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -import "@halo-dev/search-widget"; diff --git a/packages/example/tsconfig.json b/packages/example/tsconfig.json deleted file mode 100644 index 596e2cf..0000000 --- a/packages/example/tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "include": ["src"] -} diff --git a/packages/example/vite.config.ts b/packages/example/vite.config.ts deleted file mode 100644 index ddaccd0..0000000 --- a/packages/example/vite.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { fileURLToPath, URL } from "node:url"; - -import { defineConfig } from "vite"; -export default defineConfig({ - resolve: { - alias: { - "@": fileURLToPath(new URL("./src", import.meta.url)), - }, - }, - server: { - port: 4000, - }, -}); diff --git a/packages/search-widget/.eslintrc.json b/packages/search-widget/.eslintrc.json index 6dd40c1..b5e3123 100644 --- a/packages/search-widget/.eslintrc.json +++ b/packages/search-widget/.eslintrc.json @@ -3,7 +3,8 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" + "plugin:@typescript-eslint/recommended", + "@unocss" ], "parser": "@typescript-eslint/parser", "parserOptions": { diff --git a/packages/search-widget/index.html b/packages/search-widget/index.html new file mode 100644 index 0000000..e6f4cec --- /dev/null +++ b/packages/search-widget/index.html @@ -0,0 +1,66 @@ + + + + + + Search Widget Preview + + + + + + + + + + + diff --git a/packages/search-widget/package.json b/packages/search-widget/package.json index 0fd3e2f..cb32eb6 100644 --- a/packages/search-widget/package.json +++ b/packages/search-widget/package.json @@ -17,17 +17,24 @@ "var.css" ], "scripts": { - "dev": "tsc -w", - "build": "tsc", - "lint": "lit-analyzer && eslint 'src/**/*.ts'", + "dev": "vite --config vite.config.dev.ts", + "build": "vite build --config vite.config.lib.ts", + "lint": "lit-analyzer && eslint 'src/**/*.ts' --fix", "prettier": "prettier \"**/*.{cjs,html,js,json,md,ts}\" --ignore-path ./.gitignore --write" }, "dependencies": { - "lit": "^3.1.4", - "lodash-es": "^4.17.21" + "lit": "^3.2.0", + "lodash-es": "^4.17.21", + "overlayscrollbars": "^2.10.0" }, "devDependencies": { + "@iconify/json": "^2.2.234", + "@julr/unocss-preset-forms": "^0.1.0", "@types/lodash-es": "^4.17.12", - "lit-analyzer": "^1.2.1" + "@unocss/eslint-config": "^0.61.9", + "@unocss/reset": "^0.61.9", + "lit-analyzer": "^1.2.1", + "unocss": "^0.61.9", + "vite-plugin-dts": "4.0.0-beta.2" } } diff --git a/packages/search-widget/src/constants/index.ts b/packages/search-widget/src/constants/index.ts new file mode 100644 index 0000000..f025308 --- /dev/null +++ b/packages/search-widget/src/constants/index.ts @@ -0,0 +1,17 @@ +export const HISTORY_KEY = 'halo:search-widgets:history:hits'; +export const MAX_HISTORY_ITEMS = 50; + +export const SHORTCUT_HELP_LIST = [ + { + text: '选择', + kbdIcons: ['i-lucide-arrow-up', 'i-lucide-arrow-down'], + }, + { + text: '确认', + kbdIcons: ['i-lucide-corner-down-left'], + }, + { + text: '关闭', + kbdIcons: ['i-mdi-keyboard-esc'], + }, +]; diff --git a/packages/search-widget/src/search-form.ts b/packages/search-widget/src/search-form.ts index 46ecb1d..c50caeb 100644 --- a/packages/search-widget/src/search-form.ts +++ b/packages/search-widget/src/search-form.ts @@ -1,13 +1,16 @@ import { HaloDocument, SearchOption, SearchResult } from '@halo-dev/api-client'; -import { LitElement, PropertyValueMap, css, html } from 'lit'; +import resetStyles from '@unocss/reset/tailwind.css?inline'; +import { LitElement, css, html, unsafeCSS } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { classMap } from 'lit/directives/class-map.js'; import { Ref, createRef, ref } from 'lit/directives/ref.js'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; -import type { DebouncedFunc } from 'lodash-es'; -import { debounce } from 'lodash-es'; +import { DebouncedFunc, debounce, uniqBy } from 'lodash-es'; +import { + HISTORY_KEY, + MAX_HISTORY_ITEMS, + SHORTCUT_HELP_LIST, +} from './constants'; import baseStyles from './styles/base'; -import varStyles from './styles/var'; @customElement('search-form') export class SearchForm extends LitElement { @@ -17,6 +20,9 @@ export class SearchForm extends LitElement { @property({ type: Object }) options = {}; + @state() + private keyword = ''; + @state() private searchResult?: SearchResult; @@ -26,88 +32,179 @@ export class SearchForm extends LitElement { @state() private selectedIndex = 0; + @state() + private historyHits: HaloDocument[] = []; + inputRef: Ref = createRef(); constructor() { super(); this.addEventListener('keydown', this.handleKeydown); + + this.historyHits = JSON.parse( + localStorage.getItem(HISTORY_KEY) || '[]' + ) as HaloDocument[]; + + setTimeout(() => { + this.inputRef.value?.focus(); + }, 0); } override render() { return html` -
- +
+
+ + + ${this.keyword + ? html` + + ` + : ''} +
-
- ${!this.loading && this.searchResult?.hits?.length === 0 - ? html`
- 没有搜索结果 -
` - : ''} - ${this.loading - ? html`
搜索中...
` - : html` -
    - ${this.searchResult?.hits?.map( - (hit, index) => - html`
  • -
    -

    - ${unsafeHTML(hit.title)} -

    -

    - ${unsafeHTML(hit.content)} -

    -
    -
  • ` - )} -
- `} + + ${this.keyword ? this.renderItems() : this.renderHistoryItems()} + +
+ ${SHORTCUT_HELP_LIST.map( + (item) => html` +
+ ${item.kbdIcons.map( + (icon) => html` + + + + ` + )} + ${item.text} +
+ ` + )}
-
-
- 选择 - - -
+ `; + } -
- 确认 - Enter -
+ handleClearInput() { + this.keyword = ''; + this.searchResult = undefined; + this.inputRef.value!.value = ''; + this.inputRef.value!.focus(); + } -
- 关闭 - Esc -
+ renderItems() { + return html`
+ ${!this.searchResult?.hits?.length ? this.renderEmpty() : ''} +
    + ${this.searchResult?.hits?.map((hit, index) => + this.renderListItem(hit, index, 'i-lucide-file-text') + )} +
+
`; + } + + renderEmpty() { + return html`
+ 没有搜索结果 +
`; + } + + renderHistoryItems() { + return html` +
+ ${this.historyHits.length + ? html`
+

搜索历史

+ + 清除历史 + +
+
    + ${this.historyHits?.map((hit, index) => + this.renderListItem(hit, index, 'i-lucide-history') + )} +
` + : this.renderEmpty()}
`; } - protected override firstUpdated( - _changedProperties: PropertyValueMap | Map - ) { - super.firstUpdated(_changedProperties); - this.inputRef.value?.focus(); + renderListItem(hit: HaloDocument, index: number, listIcon: string) { + return html` +
  • (this.selectedIndex = index)} + class="shadow-sm flex items-center space-x-3 rounded-base cursor-pointer p-3 bg-hit [&_mark]:text-primary [&_mark]:font-semibold [&_mark]:bg-transparent ${index === + this.selectedIndex + ? '!bg-primary [&_mark]:!text-white [&_mark]:underline' + : ''}" + data-index=${index} + > + +
    +

    + ${unsafeHTML(hit.title)} +

    + ${hit.description + ? html` +

    + ${unsafeHTML(hit.description)} +

    + ` + : ''} +
    + +
  • + `; } onInput(e: InputEvent) { const input = e.target as HTMLInputElement; const value = input.value; + this.keyword = value || ''; + this.selectedIndex = 0; if (value === '') { @@ -119,6 +216,11 @@ export class SearchForm extends LitElement { this.fetchHits(value); } + handleClearHistory() { + localStorage.removeItem(HISTORY_KEY); + this.historyHits = []; + } + fetchHits: DebouncedFunc<(keyword: string) => Promise> = debounce( async (keyword: string) => { const searchOptions: SearchOption = { @@ -148,159 +250,61 @@ export class SearchForm extends LitElement { ); handleOpenLink(hit: HaloDocument) { + const updatedHistory = uniqBy([hit, ...this.historyHits], 'id').slice( + 0, + MAX_HISTORY_ITEMS + ); + localStorage.setItem(HISTORY_KEY, JSON.stringify(updatedHistory)); + this.historyHits = updatedHistory; window.location.href = hit.permalink; } handleKeydown = (e: KeyboardEvent) => { const { key, ctrlKey } = e; + const hits = this.keyword ? this.searchResult?.hits : this.historyHits; + + if (!hits) { + return; + } + if (key === 'ArrowUp' || (key === 'k' && ctrlKey)) { this.selectedIndex = Math.max(0, this.selectedIndex - 1); + this.handleScrollIntoSelected(); e.preventDefault(); } if (key === 'ArrowDown' || (key === 'j' && ctrlKey)) { - this.selectedIndex = Math.min( - this.searchResult?.hits?.length || 0, - this.selectedIndex + 1 - ); + this.selectedIndex = Math.min(hits.length || 0, this.selectedIndex + 1); + this.handleScrollIntoSelected(); e.preventDefault(); } if (key === 'Enter') { - const hit = this.searchResult?.hits?.[this.selectedIndex - 1]; + const hit = hits[this.selectedIndex]; if (hit) { this.handleOpenLink(hit); } } }; + handleScrollIntoSelected() { + const selectedElement = this.shadowRoot?.querySelector( + `[data-index="${this.selectedIndex}"]` + ); + if (selectedElement) { + selectedElement.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + } + } + static override styles = [ - varStyles, + unsafeCSS(resetStyles), baseStyles, css` - .search-form__input { - border-bottom-width: 1px; - border-color: var(--color-form-divider); - padding: 0.625em 1em; - position: sticky; - top: 0; - background-color: var(--color-form-input-bg); - } - - .search-form__input input { - width: 100%; - padding: 0.25em 0px; - outline: 2px solid transparent; - outline-offset: 2px; - border: none; - font-size: 1em; - line-height: 1.5em; - background-color: var(--color-form-input-bg); - color: var(--color-form-input); - } - - .search-form__input input::placeholder { - color: var(--color-form-input-placeholder); - } - - .search-form__result { - padding: 0.625em 0.5em; - } - - .search-form__empty, - .search-form__loading { - display: flex; - align-items: center; - justify-content: center; - font-size: 0.875em; - line-height: 1.25em; - color: var(--color-result-empty); - } - - .search-form__result-wrapper { - box-sizing: border-box; - display: flex; - width: 100%; - height: 100%; - flex-direction: column; - gap: 0.25em; - list-style: none; - margin: 0; - padding: 0; - } - - .search-form__result-wrapper li { - cursor: pointer; - } - - .search-form__result-item { - display: flex; - flex-direction: column; - gap: 0.25em; - border-radius: 0.375em; - background-color: var(--color-result-item-bg); - padding: 0.5em 0.625em; - } - - .search-form__result-item:hover, - .search-form__result-item.selected { - background-color: var(--color-result-item-hover-bg); - } - - .search-form__result-item-title { - font-size: 0.875em; - line-height: 1.25em; - font-weight: 600; - padding: 0; - margin: 0; - color: var(--color-result-item-title); - } - - .search-form__result-item-content { - font-size: 0.75em; - line-height: 1em; - color: var(--color-result-item-content); - padding: 0; - margin: 0; - } - - .search-form__result-item-content img { - width: 50%; - } - - .search-form__commands { - border-top-width: 1px; - border-color: var(--color-form-divider); - padding: 0.625em 1em; - display: flex; - justify-content: flex-end; - } - - .search-form__commands-item { - display: inline-flex; - align-items: center; - margin-left: 1.25em; - } - - .search-form__commands-item span { - font-size: 0.75em; - line-height: 1em; - color: var(--color-command-kbd-item); - } - - .search-form__commands-item kbd { - color: var(--color-command-kbd-item); - font-size: 10px; - text-align: center; - padding: 0.125em 0.3em; - border-width: 1px; - border-radius: 0.25em; - border-color: var(--color-command-kbd-border); - min-width: 1.25em; - margin-left: 0.3em; - box-shadow: 0 0 #0000, 0 0 #0000, 0 1px 2px 0 rgb(0 0 0 / 0.05); - } + @unocss-placeholder; `, ]; } diff --git a/packages/search-widget/src/search-modal.ts b/packages/search-widget/src/search-modal.ts index 41bd471..b0edb61 100644 --- a/packages/search-widget/src/search-modal.ts +++ b/packages/search-widget/src/search-modal.ts @@ -1,8 +1,10 @@ -import { LitElement, css, html } from 'lit'; +import resetStyles from '@unocss/reset/tailwind.css?inline'; +import { LitElement, PropertyValues, css, html, unsafeCSS } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; +import { OverlayScrollbars } from 'overlayscrollbars'; +import overlayscrollbarsStyles from 'overlayscrollbars/styles/overlayscrollbars.css?inline'; import './search-form'; -import varStyles from './styles/var'; import baseStyles from './styles/base'; @customElement('search-modal') @@ -18,13 +20,46 @@ export class SearchModal extends LitElement { @property({ type: Object }) options = {}; + constructor() { + super(); + + setTimeout(() => { + const modalContent = this.shadowRoot?.querySelector( + '.modal__content' + ) as HTMLElement; + if (modalContent) { + OverlayScrollbars(modalContent, { + scrollbars: { + autoHide: 'scroll', + autoHideDelay: 600, + }, + }); + } + }, 0); + } + + override willUpdate(changedProperties: PropertyValues) { + if (!changedProperties.has('open')) { + return; + } + + if (this.open) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.removeProperty('overflow'); + } + } + override render() { return html`