From 330df531958d29103e7db995fa6bea31653abb38 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 9 Nov 2026 17:35:52 +0800 Subject: [PATCH] feat(creator-studio): multi-select category picker and creatable tag input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 分类从平铺复选标签改为带搜索的多选下拉(触发器内是已选令牌), 标签改为令牌输入框:回车 / 逗号 / 顿号 / 分号 / 换行切分,输入时列出 站点已有标签作为建议,面板内可一键新建。 - 抽出 useAnchoredPanel,工具条下拉与新的选择器共用同一套 portal 定位 - bootstrap 的 tags 增加 suggestions(按使用量取前 200 个已有标签) - 清理 .jio-creator-inline 与 chip 内的死样式 Co-Authored-By: Claude Opus 5 (1M context) --- assets/creator-studio/scripts/preview.cjs | 2 +- .../src/components/category-select.tsx | 159 ++++++++ .../src/components/publish-settings.tsx | 93 +---- .../src/components/tag-input.tsx | 194 ++++++++++ .../src/components/tool-menu.tsx | 69 +--- .../src/hooks/use-anchored-panel.ts | 133 +++++++ assets/creator-studio/src/index.tsx | 5 +- .../src/styles/_publish-drawer.scss | 354 ++++++++++++++++-- src/Core/CreatorRest.php | 19 +- .../creator-studio-contract.test.cjs | 30 ++ 10 files changed, 888 insertions(+), 170 deletions(-) create mode 100644 assets/creator-studio/src/components/category-select.tsx create mode 100644 assets/creator-studio/src/components/tag-input.tsx create mode 100644 assets/creator-studio/src/hooks/use-anchored-panel.ts diff --git a/assets/creator-studio/scripts/preview.cjs b/assets/creator-studio/scripts/preview.cjs index 41e36103..97a334df 100644 --- a/assets/creator-studio/scripts/preview.cjs +++ b/assets/creator-studio/scripts/preview.cjs @@ -32,7 +32,7 @@ const mockBootstrap = { ], selected: [1], }, - tags: { items: ['写作', '产品'], enabled: true }, + tags: { items: ['写作', '产品'], enabled: true, suggestions: ['写作', '产品', '前端', 'WordPress', '设计'] }, custom: {}, customDefs: [], featured: { type: '', data: {}, canImage: true, canSlide: false, canVideo: false }, diff --git a/assets/creator-studio/src/components/category-select.tsx b/assets/creator-studio/src/components/category-select.tsx new file mode 100644 index 00000000..47690b5c --- /dev/null +++ b/assets/creator-studio/src/components/category-select.tsx @@ -0,0 +1,159 @@ +/** + * 分类多选下拉。 + * + * 触发器本身就是「已选令牌 + 占位文字」的输入框外观,点开后是带搜索的复选列表: + * 作者不用在一屏几十个分类里横向找,输入关键词就能定位。面板 portal 到 body, + * 避免被发布设置卡片或页面容器裁剪。 + * + * @package Jio + */ + +import { createPortal, useEffect, useMemo, useState } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; + +import { useAnchoredPanel } from '../hooks/use-anchored-panel'; + +export type CategoryOption = { id: number; name: string }; + +type Props = { + options: CategoryOption[]; + value: number[]; + onChange: (ids: number[]) => void; +}; + +export function CategorySelect({ options, value, onChange }: Props) { + const [open, setOpen] = useState(false); + const [query, setQuery] = useState(''); + const { triggerRef, panelRef, position } = useAnchoredPanel({ + open, + width: 320, + align: 'start', + followScroll: true, + onDismiss: () => setOpen(false), + }); + + const selected = useMemo(() => new Set(value), [value]); + const chosen = useMemo( + () => options.filter((option) => selected.has(option.id)), + [options, selected] + ); + const filtered = useMemo(() => { + const keyword = query.trim().toLowerCase(); + if (keyword === '') { + return options; + } + return options.filter((option) => option.name.toLowerCase().includes(keyword)); + }, [options, query]); + + // 每次打开都从干净的搜索词开始,避免上次的过滤条件把列表藏起来。 + useEffect(() => { + if (!open) { + setQuery(''); + } + }, [open]); + + const toggle = (id: number) => { + onChange( + selected.has(id) ? value.filter((item) => item !== id) : [...value, id] + ); + }; + + const panel = open + ? createPortal( +
+
+