diff --git a/.changeset/slimy-parks-say.md b/.changeset/slimy-parks-say.md new file mode 100644 index 000000000..4dc09b933 --- /dev/null +++ b/.changeset/slimy-parks-say.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Narrow a table or card grid to the reader's content selection: choosing an option in a tab or a select button now filters any select column that offers it. The table says what it has been narrowed to, and offers a Clear that drops the selection. diff --git a/packages/gitbook/src/components/DocumentView/Table/Table.tsx b/packages/gitbook/src/components/DocumentView/Table/Table.tsx index 9ecf7dde3..ed6d391bc 100644 --- a/packages/gitbook/src/components/DocumentView/Table/Table.tsx +++ b/packages/gitbook/src/components/DocumentView/Table/Table.tsx @@ -15,7 +15,12 @@ import { } from './search'; import { shouldShowTableSearch } from './shouldShowSearch'; import { StickyViewGrid } from './StickyViewGrid'; -import { TableSearchEmpty, TableSearchInput, TableSearchProvider } from './TableSearch'; +import { + TableSearchEmpty, + TableSearchInput, + TableSearchProvider, + TableSelectionFilter, +} from './TableSearch'; import { ViewCards } from './ViewCards'; import { ViewGrid, ViewGridHeader } from './ViewGrid'; import { tcls } from '@/lib/tailwind'; @@ -48,9 +53,14 @@ export function Table(props: BlockProps) { searchOverride: block.data.search, isPrint: context.mode === 'print', }); - const searchRecords = showSearch - ? records.map(([id, record]) => ({ id, ...getTableRecordSearchData(block, record) })) - : []; + const selectColumns = getTableSelectColumns(block); + // Also needed when the search bar is hidden: a reader's content selection can narrow a select + // column from a tab or picker elsewhere on the page, and without records there is nothing to + // match against. A table with no select column can never be narrowed that way, so it skips. + const searchRecords = + showSearch || selectColumns.length > 0 + ? records.map(([id, record]) => ({ id, ...getTableRecordSearchData(block, record) })) + : []; const cellMergeLayout = createTableCellMergeLayout( block, records.map(([recordId]) => recordId) @@ -59,6 +69,7 @@ export function Table(props: BlockProps) { return ( ) {
{showSearch ? ( ) : null} + {/* Sits under the filter control it relates to, and tight against the search bar, + so it reads as part of the filter controls rather than a caption on the table. + Standalone — cards, short grids — there is no control to sit under. */} + | null; /** True when there are records but the active filters match none of them. */ isEmpty: boolean; + /** + * Select columns the reader's content selection narrowed and the reader has left alone. + * A column they have since filtered themselves drops out, since the table no longer shows it. + */ + slugFilter: SlugFilterEntry[]; + /** Drop the selection driving {@link slugFilter}, site-wide. The only thing that clears one. */ + clearSlugFilter: () => void; }; const TableSearchContext = React.createContext(null); @@ -47,16 +62,32 @@ const TableSearchContext = React.createContext(n export function TableSearchProvider(props: { records?: TableSearchRecordData[]; recordGroups?: readonly (readonly string[])[]; + /** Select columns of this table, so the reader's content selection can narrow them. */ + selectColumns?: TableSelectColumn[]; children: React.ReactNode; }) { - const { records = [], recordGroups = [] } = props; + const { records = [], recordGroups = [], selectColumns = [] } = props; const [query, setQuery] = React.useState(''); const [selectedOptions, setSelectedOptions] = React.useState(() => ({})); const [checkedColumns, setCheckedColumns] = React.useState>( () => new Set() ); + // The selection that drives this lives outside the table — a tab, a select button or a picker + // elsewhere on the page — so this synchronises with it rather than deriving from it. + const slugFilter = useSlugFilter(selectColumns); + + // Columns the selection narrowed last time round. Kept so a new selection — or clearing it — + // undoes the previous one, rather than leaving a filter the reader can no longer account for. + const narrowedColumns = React.useRef([]); + const toggleOption = React.useCallback((column: string, value: string) => { + // The reader is taking this column over, so the selection no longer owns it: a later clear + // must leave their choice of options alone. Their change stays local — the filter is this + // table's, for this visit, while the selection is site-wide and persists, so only the + // clear beside the notice touches it. + narrowedColumns.current = narrowedColumns.current.filter((narrowed) => narrowed !== column); + setSelectedOptions((previous) => { const values = new Set(previous[column]); if (values.has(value)) { @@ -87,6 +118,33 @@ export function TableSearchProvider(props: { }); }, []); + React.useEffect(() => { + const previouslyNarrowed = narrowedColumns.current; + narrowedColumns.current = slugFilter.map((entry) => entry.column); + + setSelectedOptions((previous) => + reconcileSelectedOptions(previous, previouslyNarrowed, slugFilter) + ); + }, [slugFilter]); + + // What the notice may speak for: the columns the selection narrowed and the reader has left + // alone. A column they have since filtered themselves still has an active slug, but the table + // is no longer showing it, so the notice must not claim it. + const appliedSlugFilter = React.useMemo( + () => getAppliedSlugFilter(slugFilter, selectedOptions), + [slugFilter, selectedOptions] + ); + + // Clearing goes through the store rather than local state: the selection is what persists, so + // only dropping it there stops the filter coming back on the next load. It is site-wide, so a + // tab elsewhere on the page reverts to its default too. This is the only thing that clears a + // selection — changing the filter never does. + const clearSlugFilter = React.useCallback(() => { + for (const entry of appliedSlugFilter) { + selectStore.deactivate(entry.slug); + } + }, [appliedSlugFilter]); + // Match every record once, here, rather than in each row — rows just look themselves up by id. const visibleIds = React.useMemo( () => @@ -112,8 +170,20 @@ export function TableSearchProvider(props: { toggleCheckbox, visibleIds, isEmpty, + slugFilter: appliedSlugFilter, + clearSlugFilter, }), - [query, selectedOptions, toggleOption, checkedColumns, toggleCheckbox, visibleIds, isEmpty] + [ + query, + selectedOptions, + toggleOption, + checkedColumns, + toggleCheckbox, + visibleIds, + isEmpty, + appliedSlugFilter, + clearSlugFilter, + ] ); return ( @@ -121,6 +191,40 @@ export function TableSearchProvider(props: { ); } +/** + * The reader's selection, reduced to the columns of *this* table. + * + * Subscribes once and returns a string rather than an object: `useSyncExternalStore` compares + * snapshots by identity, so a fresh object each call would loop. It also means a selection that + * changes nothing for this table re-renders nothing — the reason `useSelect` stopped exposing the + * recency list in the first place. + */ +function useSlugFilter(selectColumns: TableSelectColumn[]): SlugFilterEntry[] { + const columnsKey = selectColumns + .map( + (column) => + `${column.id}:${column.options.map((option) => `${option.value}=${option.label}`).join('|')}` + ) + .join(';'); + + const getKey = React.useCallback( + () => slugFilterKey(resolveSlugFilter(selectColumns, selectStore.getState().slugs)), + // `selectColumns` is a fresh array each render; its contents are what matter. + // oxlint-disable-next-line react-hooks/exhaustive-deps + [columnsKey] + ); + + const filterKey = React.useSyncExternalStore(selectStore.subscribe, getKey, getKey); + + // The key is only an identity: rebuild the filter itself when it moves, rather than parsing the + // key back apart, since an option value can be any string an import gave it. + return React.useMemo( + () => resolveSlugFilter(selectColumns, selectStore.getState().slugs), + // oxlint-disable-next-line react-hooks/exhaustive-deps + [filterKey] + ); +} + function useTableSearch(): TableSearchContextValue { const context = React.useContext(TableSearchContext); if (!context) { @@ -193,6 +297,49 @@ export function TableSearchEmpty(props: { className?: ClassValue }) { ); } +/** + * Names the selection narrowing this table, and lets the reader drop it. + * + * Deliberately worded around the *selection* rather than the filter: the column dropdown beside it + * shows the same column as active, but clearing there only resets local state and the filter returns + * on the next load. This is the control that actually undoes it. + * + * Rendered independently of the search bar. `shouldShowTableSearch` leaves the filter controls off + * cards, off grids below the row threshold, and off any table whose author turned search off — and + * in every one of those a narrowed table would otherwise just read as missing rows. + */ +export function TableSelectionFilter(props: { className?: ClassValue }) { + const language = useLanguage(); + const { slugFilter, clearSlugFilter } = useTableSearch(); + + if (slugFilter.length === 0) { + return null; + } + + return ( +
+ + + {tString( + language, + 'table_filtered_by_selection', + slugFilter.map((entry) => entry.label).join(', ') + )} + +
+ ); +} + /** * A blank multi-select dropdown button for a single select column. Becomes `active` while any * of its options are selected. diff --git a/packages/gitbook/src/components/DocumentView/Table/slugFilter.test.ts b/packages/gitbook/src/components/DocumentView/Table/slugFilter.test.ts new file mode 100644 index 000000000..57a4ac688 --- /dev/null +++ b/packages/gitbook/src/components/DocumentView/Table/slugFilter.test.ts @@ -0,0 +1,217 @@ +import { describe, expect, it } from 'bun:test'; + +import type { TableSelectColumn } from './search'; +import { + getAppliedSlugFilter, + getOptionSlug, + reconcileSelectedOptions, + resolveSlugFilter, + slugFilterKey, +} from './slugFilter'; + +/** + * A select column as the editor writes one: each option carries an opaque generated `value` and the + * author's wording as its `label`. Tests must not conflate the two — matching on `value` looks + * right against fixtures that reuse the label and matches nothing against real content. + */ +function column(id: string, labels: string[]): TableSelectColumn { + return { + id, + label: id, + options: labels.map((label, index) => ({ + value: `${id}-key-${index}`, + label, + color: 'blue', + })), + }; +} + +/** The opaque value of the option an author labelled `label`. */ +function valueOf(column: TableSelectColumn, label: string): string { + const option = column.options.find((option) => option.label === label); + if (!option) { + throw new Error(`no option labelled ${label}`); + } + return option.value; +} + +describe('resolveSlugFilter', () => { + const platform = column('platform', ['macOS', 'Windows', 'Linux']); + const status = column('status', ['Done', 'To do']); + + it('matches the option label, and resolves to its opaque value', () => { + expect(resolveSlugFilter([platform], ['macos'])).toEqual([ + { + column: 'platform', + value: valueOf(platform, 'macOS'), + label: 'macOS', + slug: 'macos', + }, + ]); + }); + + it('carries the label and slug needed to show and clear the filter', () => { + // The reader is told what narrowed the table, and the slug is what clearing deactivates. + const [entry] = resolveSlugFilter([platform], ['windows']); + expect(entry?.label).toBe('Windows'); + expect(entry?.slug).toBe('windows'); + }); + + it('never matches the opaque value itself', () => { + expect(resolveSlugFilter([platform], [valueOf(platform, 'macOS')])).toEqual([]); + }); + + it('slugifies the label the way every other select surface does', () => { + const languages = column('language', ['Node.js', 'C++', 'Windows 10']); + expect(resolveSlugFilter([languages], ['node.js'])[0]?.label).toBe('Node.js'); + expect(resolveSlugFilter([languages], ['c++'])[0]?.label).toBe('C++'); + expect(resolveSlugFilter([languages], ['windows-10'])[0]?.label).toBe('Windows 10'); + }); + + it('falls back to the value when an option has no label', () => { + const legacy: TableSelectColumn = { + id: 'legacy', + label: 'legacy', + options: [{ value: 'macos', label: '', color: 'blue' }], + }; + expect(resolveSlugFilter([legacy], ['macos'])).toEqual([ + { column: 'legacy', value: 'macos', label: 'macos', slug: 'macos' }, + ]); + }); + + it('leaves a column the selection says nothing about', () => { + const entries = resolveSlugFilter([platform, status], ['macos']); + expect(entries.map((entry) => entry.column)).toEqual(['platform']); + }); + + it('narrows several columns at once when the selection covers both', () => { + const entries = resolveSlugFilter([platform, status], ['macos', 'done']); + expect(entries.map((entry) => entry.value)).toEqual([ + valueOf(platform, 'macOS'), + valueOf(status, 'Done'), + ]); + }); + + it('takes the most recently activated when a column offers several active options', () => { + // Most-recent-first, so `windows` wins over `macos` — the rule tabs resolve with. + expect(resolveSlugFilter([platform], ['windows', 'macos'])[0]?.value).toBe( + valueOf(platform, 'Windows') + ); + expect(resolveSlugFilter([platform], ['macos', 'windows'])[0]?.value).toBe( + valueOf(platform, 'macOS') + ); + }); + + it('filters nothing when the selection matches no column', () => { + expect(resolveSlugFilter([platform, status], ['python'])).toEqual([]); + expect(resolveSlugFilter([platform], [])).toEqual([]); + expect(resolveSlugFilter([], ['macos'])).toEqual([]); + }); +}); + +describe('getOptionSlug', () => { + // Shared by the matcher and by the write-back that moves the selection when a reader changes a + // governed column, so the two can never disagree about what an option answers to. + it('slugifies the label, and falls back to the value', () => { + expect(getOptionSlug({ value: 'key-0', label: 'macOS' })).toBe('macos'); + expect(getOptionSlug({ value: 'key-1', label: 'Windows 10' })).toBe('windows-10'); + expect(getOptionSlug({ value: 'macos', label: '' })).toBe('macos'); + }); +}); + +describe('reconcileSelectedOptions', () => { + const entry = (column: string, value: string) => ({ + column, + value, + label: value, + slug: value, + }); + + it('drops the column when the selection is cleared', () => { + const previous = { platform: new Set(['macos-value']) }; + expect(reconcileSelectedOptions(previous, ['platform'], [])).toEqual({}); + }); + + it('leaves filters the reader set themselves', () => { + const previous = { + platform: new Set(['macos-value']), + status: new Set(['done-value']), + }; + expect(reconcileSelectedOptions(previous, ['platform'], [])).toEqual({ + status: new Set(['done-value']), + }); + }); + + it('replaces the previous selection rather than adding to it', () => { + const previous = { platform: new Set(['macos-value']) }; + expect( + reconcileSelectedOptions(previous, ['platform'], [entry('platform', 'windows-value')]) + ).toEqual({ platform: new Set(['windows-value']) }); + }); + + it('gives the same answer however many times it is applied', () => { + // React may invoke a state updater more than once with the same input. An earlier version + // tracked the narrowed columns inside the updater, so the second pass saw them already + // cleared, took the early return and handed back the *unchanged* state — silently undoing + // a clear while leaving an apply working, which is exactly how it presented. + const previous = { platform: new Set(['macos-value']) }; + const once = reconcileSelectedOptions(previous, ['platform'], []); + const twice = reconcileSelectedOptions(previous, ['platform'], []); + expect(twice).toEqual(once); + }); + + it('is a no-op when there is nothing to narrow and nothing to undo', () => { + const previous = { status: new Set(['done-value']) }; + expect(reconcileSelectedOptions(previous, [], [])).toBe(previous); + }); +}); + +describe('getAppliedSlugFilter', () => { + const macos = { column: 'platform', value: 'macos-value', label: 'macOS', slug: 'macos' }; + + it('speaks for a column the reader has left alone', () => { + const selected = { platform: new Set(['macos-value']) }; + expect(getAppliedSlugFilter([macos], selected)).toEqual([macos]); + }); + + it('drops a column the reader has filtered to something else', () => { + // Changing the filter deliberately leaves the selection active, so the slug is still there; + // the notice just must not claim a match the table is no longer showing. + const selected = { platform: new Set(['windows-value']) }; + expect(getAppliedSlugFilter([macos], selected)).toEqual([]); + }); + + it('drops a column the reader has widened to several options', () => { + const selected = { platform: new Set(['macos-value', 'windows-value']) }; + expect(getAppliedSlugFilter([macos], selected)).toEqual([]); + }); + + it('drops a column the reader has cleared', () => { + expect(getAppliedSlugFilter([macos], {})).toEqual([]); + }); +}); + +describe('slugFilterKey', () => { + const entry = (column: string, value: string) => ({ + column, + value, + label: value, + slug: value, + }); + + it('is stable whatever order the columns resolve in', () => { + expect(slugFilterKey([entry('platform', 'macos'), entry('status', 'done')])).toBe( + slugFilterKey([entry('status', 'done'), entry('platform', 'macos')]) + ); + }); + + it('changes when the selection moves', () => { + expect(slugFilterKey([entry('platform', 'macos')])).not.toBe( + slugFilterKey([entry('platform', 'linux')]) + ); + }); + + it('is empty when nothing is filtered', () => { + expect(slugFilterKey([])).toBe(''); + }); +}); diff --git a/packages/gitbook/src/components/DocumentView/Table/slugFilter.ts b/packages/gitbook/src/components/DocumentView/Table/slugFilter.ts new file mode 100644 index 000000000..ffda4dd7e --- /dev/null +++ b/packages/gitbook/src/components/DocumentView/Table/slugFilter.ts @@ -0,0 +1,127 @@ +import type { TableSelectColumn } from './search'; +import type { SelectedOptions } from './searchMatch'; +import { slugifySelectValue } from '@/lib/select'; + +/** One select column narrowed by the reader's content selection. */ +export interface SlugFilterEntry { + /** Id of the select column being narrowed. */ + column: string; + /** The option's opaque value, which the record matcher compares against. */ + value: string; + /** The option's author-typed label, shown to the reader. */ + label: string; + /** The active slug that narrowed the column, so the reader can clear it again. */ + slug: string; +} + +/** + * The `select` slug an option answers to. + * + * A table option's `value` is an opaque generated key, so it is the author-typed `label` that names + * it — the same wording a tab title or select button would carry, put through the same slugifier so + * a "macOS" column option and a "macOS" tab resolve to the one slug. Options with no label fall back + * to the raw value, mirroring how a cell renders one. + */ +export function getOptionSlug(option: { value: string; label: string }): string { + return slugifySelectValue(option.label || option.value); +} + +/** + * Work out which option of each select column the reader's current selection points at. + * + * A table's select column declares its own option set, so a slug only filters a column that + * actually offers it: a reader who picked `macos` narrows a Platform column to macOS and leaves a + * Status column alone. Where several of a column's options are active at once, the most recently + * activated wins — the same rule tabs resolve with, so a table and the tabs beside it agree. + */ +export function resolveSlugFilter( + columns: TableSelectColumn[], + slugs: string[] +): SlugFilterEntry[] { + const entries: SlugFilterEntry[] = []; + + for (const column of columns) { + let best: SlugFilterEntry | undefined; + let bestRank = Number.POSITIVE_INFINITY; + + for (const option of column.options) { + const slug = getOptionSlug(option); + if (!slug) { + continue; + } + + const rank = slugs.indexOf(slug); + if (rank >= 0 && rank < bestRank) { + bestRank = rank; + best = { + column: column.id, + value: option.value, + label: option.label || option.value, + slug, + }; + } + } + + if (best) { + entries.push(best); + } + } + + return entries; +} + +/** Stable identity for a filter, so it is only rebuilt when the selection actually moves. */ +export function slugFilterKey(entries: SlugFilterEntry[]): string { + return entries + .map((entry) => `${entry.column}=${entry.value}`) + .sort() + .join(','); +} + +/** + * Fold the columns the selection narrows into the reader's own filters. + * + * Kept pure, and given the previously narrowed columns rather than reading them from a ref, so it + * can be applied more than once without changing the answer — React may invoke a state updater + * twice, and an earlier version tracked those columns inside the updater itself, which silently + * undid a clear on the second pass. + * + * Only columns the selection narrowed last time are dropped; anything the reader filtered by hand + * is left exactly as it was. + */ +export function reconcileSelectedOptions( + previous: SelectedOptions, + previouslyNarrowed: readonly string[], + slugFilter: readonly SlugFilterEntry[] +): SelectedOptions { + if (previouslyNarrowed.length === 0 && slugFilter.length === 0) { + return previous; + } + + const next = { ...previous }; + for (const column of previouslyNarrowed) { + delete next[column]; + } + for (const entry of slugFilter) { + next[entry.column] = new Set([entry.value]); + } + return next; +} + +/** + * Narrow the filter to the columns the table is *still* showing. + * + * A reader can change any of these columns by hand, and that deliberately leaves the selection + * alone — the filter is this table's, for this visit, while the selection is site-wide and + * persists. So the notice has to stop speaking for a column that no longer matches, rather than + * describing a view the reader has since changed. + */ +export function getAppliedSlugFilter( + slugFilter: readonly SlugFilterEntry[], + selectedOptions: SelectedOptions +): SlugFilterEntry[] { + return slugFilter.filter((entry) => { + const values = selectedOptions[entry.column]; + return values?.size === 1 && values.has(entry.value); + }); +} diff --git a/packages/gitbook/src/intl/translations/ar.ts b/packages/gitbook/src/intl/translations/ar.ts index a74120810..6105037ff 100644 --- a/packages/gitbook/src/intl/translations/ar.ts +++ b/packages/gitbook/src/intl/translations/ar.ts @@ -11,6 +11,8 @@ export const ar: TranslationLanguage = { switch_to_system_theme: 'التبديل إلى سمة النظام', search: 'بحث', clear: 'مسح', + table_filtered_by_selection: 'يطابق الاختيار: ${1}', + table_clear_selection: 'مسح الاختيار', tags: 'الوسوم', search_back: 'العودة إلى نتائج البحث', search_or_ask: 'اسأل أو ابحث', diff --git a/packages/gitbook/src/intl/translations/bg.ts b/packages/gitbook/src/intl/translations/bg.ts index 3684b9e1c..960ff0327 100644 --- a/packages/gitbook/src/intl/translations/bg.ts +++ b/packages/gitbook/src/intl/translations/bg.ts @@ -11,6 +11,8 @@ export const bg: TranslationLanguage = { switch_to_system_theme: 'Превключване към системната тема', search: 'Търсене', clear: 'Изчистване', + table_filtered_by_selection: 'Съответства на избора: ${1}', + table_clear_selection: 'Изчисти избора', tags: 'Етикети', search_back: 'Назад към резултатите от търсенето', search_or_ask: 'Попитайте или търсете', diff --git a/packages/gitbook/src/intl/translations/cs.ts b/packages/gitbook/src/intl/translations/cs.ts index 8736c8958..96d95e293 100644 --- a/packages/gitbook/src/intl/translations/cs.ts +++ b/packages/gitbook/src/intl/translations/cs.ts @@ -11,6 +11,8 @@ export const cs: TranslationLanguage = { switch_to_system_theme: 'Přepnout na systémový motiv', search: 'Hledat', clear: 'Vymazat', + table_filtered_by_selection: 'Odpovídá výběru: ${1}', + table_clear_selection: 'Zrušit výběr', tags: 'Štítky', search_back: 'Zpět na výsledky hledání', search_or_ask: 'Zeptat se nebo hledat', diff --git a/packages/gitbook/src/intl/translations/da.ts b/packages/gitbook/src/intl/translations/da.ts index 3b6c9172b..6a1e8594e 100644 --- a/packages/gitbook/src/intl/translations/da.ts +++ b/packages/gitbook/src/intl/translations/da.ts @@ -11,6 +11,8 @@ export const da: TranslationLanguage = { switch_to_system_theme: 'Skift til systemtema', search: 'Søg', clear: 'Ryd', + table_filtered_by_selection: 'Matcher valg: ${1}', + table_clear_selection: 'Ryd valg', tags: 'Tags', search_back: 'Tilbage til søgeresultater', search_or_ask: 'Spørg eller søg', diff --git a/packages/gitbook/src/intl/translations/de.ts b/packages/gitbook/src/intl/translations/de.ts index a909a1f85..3ad52e9a6 100644 --- a/packages/gitbook/src/intl/translations/de.ts +++ b/packages/gitbook/src/intl/translations/de.ts @@ -11,6 +11,8 @@ export const de: TranslationLanguage = { switch_to_system_theme: 'Zum Systemmodus wechseln', search: 'Suche', clear: 'Löschen', + table_filtered_by_selection: 'Entspricht Auswahl: ${1}', + table_clear_selection: 'Auswahl zurücksetzen', tags: 'Tags', search_back: 'Zurück zu den Suchergebnissen', search_or_ask: 'Fragen oder Suchen', diff --git a/packages/gitbook/src/intl/translations/el.ts b/packages/gitbook/src/intl/translations/el.ts index b6694749c..6dfa44898 100644 --- a/packages/gitbook/src/intl/translations/el.ts +++ b/packages/gitbook/src/intl/translations/el.ts @@ -11,6 +11,8 @@ export const el: TranslationLanguage = { switch_to_system_theme: 'Αλλαγή στο θέμα συστήματος', search: 'Αναζήτηση', clear: 'Εκκαθάριση', + table_filtered_by_selection: 'Ταιριάζει με την επιλογή: ${1}', + table_clear_selection: 'Εκκαθάριση επιλογής', tags: 'Ετικέτες', search_back: 'Επιστροφή στα αποτελέσματα αναζήτησης', search_or_ask: 'Ρωτήστε ή αναζητήστε', diff --git a/packages/gitbook/src/intl/translations/en.ts b/packages/gitbook/src/intl/translations/en.ts index a5f074faf..df71a95f9 100644 --- a/packages/gitbook/src/intl/translations/en.ts +++ b/packages/gitbook/src/intl/translations/en.ts @@ -9,6 +9,8 @@ export const en = { switch_to_system_theme: 'Switch to system theme', search: 'Search', clear: 'Clear', + table_filtered_by_selection: 'Matches selection: ${1}', + table_clear_selection: 'Clear selection', tags: 'Tags', search_back: 'Back to search results', search_or_ask: 'Ask or search', diff --git a/packages/gitbook/src/intl/translations/es.ts b/packages/gitbook/src/intl/translations/es.ts index 308274f3c..d5653bb4d 100644 --- a/packages/gitbook/src/intl/translations/es.ts +++ b/packages/gitbook/src/intl/translations/es.ts @@ -11,6 +11,8 @@ export const es: TranslationLanguage = { switch_to_system_theme: 'Cambiar a tema del sistema', search: 'Buscar', clear: 'Limpiar', + table_filtered_by_selection: 'Coincide con la selección: ${1}', + table_clear_selection: 'Borrar selección', tags: 'Etiquetas', search_back: 'Volver a los resultados de búsqueda', search_or_ask: 'Preguntar o Buscar', diff --git a/packages/gitbook/src/intl/translations/et.ts b/packages/gitbook/src/intl/translations/et.ts index d3885fca8..bcc9292d7 100644 --- a/packages/gitbook/src/intl/translations/et.ts +++ b/packages/gitbook/src/intl/translations/et.ts @@ -11,6 +11,8 @@ export const et: TranslationLanguage = { switch_to_system_theme: 'Lülitu süsteemi teemale', search: 'Otsi', clear: 'Tühjenda', + table_filtered_by_selection: 'Vastab valikule: ${1}', + table_clear_selection: 'Tühjenda valik', tags: 'Sildid', search_back: 'Tagasi otsingutulemuste juurde', search_or_ask: 'Küsi või otsi', diff --git a/packages/gitbook/src/intl/translations/fi.ts b/packages/gitbook/src/intl/translations/fi.ts index 4b4620451..2531eb240 100644 --- a/packages/gitbook/src/intl/translations/fi.ts +++ b/packages/gitbook/src/intl/translations/fi.ts @@ -11,6 +11,8 @@ export const fi: TranslationLanguage = { switch_to_system_theme: 'Vaihda järjestelmän teemaan', search: 'Haku', clear: 'Tyhjennä', + table_filtered_by_selection: 'Vastaa valintaa: ${1}', + table_clear_selection: 'Tyhjennä valinta', tags: 'Tunnisteet', search_back: 'Takaisin hakutuloksiin', search_or_ask: 'Kysy tai hae', diff --git a/packages/gitbook/src/intl/translations/fr.ts b/packages/gitbook/src/intl/translations/fr.ts index b87924ab4..acc576a5b 100644 --- a/packages/gitbook/src/intl/translations/fr.ts +++ b/packages/gitbook/src/intl/translations/fr.ts @@ -11,6 +11,8 @@ export const fr: TranslationLanguage = { switch_to_system_theme: 'Utiliser le thème du système', search: 'Rechercher', clear: 'Effacer', + table_filtered_by_selection: 'Correspond à la sélection : ${1}', + table_clear_selection: 'Effacer la sélection', tags: 'Étiquettes', search_back: 'Retour aux résultats de recherche', search_or_ask: 'Rechercher', diff --git a/packages/gitbook/src/intl/translations/he.ts b/packages/gitbook/src/intl/translations/he.ts index 9614e9f92..219282c8a 100644 --- a/packages/gitbook/src/intl/translations/he.ts +++ b/packages/gitbook/src/intl/translations/he.ts @@ -11,6 +11,8 @@ export const he: TranslationLanguage = { switch_to_system_theme: 'מעבר לערכת הנושא של המערכת', search: 'חיפוש', clear: 'ניקוי', + table_filtered_by_selection: 'תואם לבחירה: ${1}', + table_clear_selection: 'נקה בחירה', tags: 'תגיות', search_back: 'חזרה לתוצאות החיפוש', search_or_ask: 'שאלה או חיפוש', diff --git a/packages/gitbook/src/intl/translations/hi.ts b/packages/gitbook/src/intl/translations/hi.ts index 78d79e4c3..24ebda70c 100644 --- a/packages/gitbook/src/intl/translations/hi.ts +++ b/packages/gitbook/src/intl/translations/hi.ts @@ -11,6 +11,8 @@ export const hi: TranslationLanguage = { switch_to_system_theme: 'सिस्टम थीम पर जाएं', search: 'खोजें', clear: 'साफ करें', + table_filtered_by_selection: 'चयन से मेल खाता है: ${1}', + table_clear_selection: 'चयन साफ़ करें', tags: 'टैग', search_back: 'खोज परिणामों पर वापस जाएं', search_or_ask: 'पूछें या खोजें', diff --git a/packages/gitbook/src/intl/translations/hr.ts b/packages/gitbook/src/intl/translations/hr.ts index 798601080..2a8328792 100644 --- a/packages/gitbook/src/intl/translations/hr.ts +++ b/packages/gitbook/src/intl/translations/hr.ts @@ -11,6 +11,8 @@ export const hr: TranslationLanguage = { switch_to_system_theme: 'Prebaci na temu sustava', search: 'Pretraži', clear: 'Očisti', + table_filtered_by_selection: 'Odgovara odabiru: ${1}', + table_clear_selection: 'Očisti odabir', tags: 'Oznake', search_back: 'Natrag na rezultate pretraživanja', search_or_ask: 'Pitaj ili pretraži', diff --git a/packages/gitbook/src/intl/translations/hu.ts b/packages/gitbook/src/intl/translations/hu.ts index 6910b3c6d..0e57021e1 100644 --- a/packages/gitbook/src/intl/translations/hu.ts +++ b/packages/gitbook/src/intl/translations/hu.ts @@ -11,6 +11,8 @@ export const hu: TranslationLanguage = { switch_to_system_theme: 'Váltás rendszer témára', search: 'Keresés', clear: 'Törlés', + table_filtered_by_selection: 'Megfelel a kijelölésnek: ${1}', + table_clear_selection: 'Kijelölés törlése', tags: 'Címkék', search_back: 'Vissza a keresési eredményekhez', search_or_ask: 'Kérdezés vagy keresés', diff --git a/packages/gitbook/src/intl/translations/id.ts b/packages/gitbook/src/intl/translations/id.ts index b8a0c4927..e9ae77387 100644 --- a/packages/gitbook/src/intl/translations/id.ts +++ b/packages/gitbook/src/intl/translations/id.ts @@ -11,6 +11,8 @@ export const id: TranslationLanguage = { switch_to_system_theme: 'Beralih ke tema sistem', search: 'Cari', clear: 'Bersihkan', + table_filtered_by_selection: 'Sesuai pilihan: ${1}', + table_clear_selection: 'Hapus pilihan', tags: 'Tag', search_back: 'Kembali ke hasil pencarian', search_or_ask: 'Tanya atau cari', diff --git a/packages/gitbook/src/intl/translations/it.ts b/packages/gitbook/src/intl/translations/it.ts index 4dd2dbc17..7260cb73b 100644 --- a/packages/gitbook/src/intl/translations/it.ts +++ b/packages/gitbook/src/intl/translations/it.ts @@ -11,6 +11,8 @@ export const it: TranslationLanguage = { switch_to_system_theme: 'Passa al tema di sistema', search: 'Cerca', clear: 'Cancella', + table_filtered_by_selection: 'Corrisponde alla selezione: ${1}', + table_clear_selection: 'Cancella selezione', tags: 'Tag', search_back: 'Torna ai risultati di ricerca', search_or_ask: 'Chiedi o cerca', diff --git a/packages/gitbook/src/intl/translations/ja.ts b/packages/gitbook/src/intl/translations/ja.ts index ea6d61c8e..37c3e34dc 100644 --- a/packages/gitbook/src/intl/translations/ja.ts +++ b/packages/gitbook/src/intl/translations/ja.ts @@ -11,6 +11,8 @@ export const ja: TranslationLanguage = { switch_to_system_theme: 'システムのテーマに切り替え', search: '検索', clear: 'クリア', + table_filtered_by_selection: '選択に一致: ${1}', + table_clear_selection: '選択をクリア', tags: 'タグ', search_back: '検索結果に戻る', search_or_ask: '質問または検索', diff --git a/packages/gitbook/src/intl/translations/ko.ts b/packages/gitbook/src/intl/translations/ko.ts index c7de755aa..f94fea69d 100644 --- a/packages/gitbook/src/intl/translations/ko.ts +++ b/packages/gitbook/src/intl/translations/ko.ts @@ -11,6 +11,8 @@ export const ko: TranslationLanguage = { switch_to_system_theme: '시스템 테마로 전환', search: '검색', clear: '지우기', + table_filtered_by_selection: '선택과 일치: ${1}', + table_clear_selection: '선택 지우기', tags: '태그', search_back: '검색 결과로 돌아가기', search_or_ask: '질문 또는 검색', diff --git a/packages/gitbook/src/intl/translations/lt.ts b/packages/gitbook/src/intl/translations/lt.ts index a001c7204..5d1b2da94 100644 --- a/packages/gitbook/src/intl/translations/lt.ts +++ b/packages/gitbook/src/intl/translations/lt.ts @@ -11,6 +11,8 @@ export const lt: TranslationLanguage = { switch_to_system_theme: 'Perjungti į sistemos temą', search: 'Ieškoti', clear: 'Išvalyti', + table_filtered_by_selection: 'Atitinka pasirinkimą: ${1}', + table_clear_selection: 'Išvalyti pasirinkimą', tags: 'Žymos', search_back: 'Grįžti į paieškos rezultatus', search_or_ask: 'Klausti arba ieškoti', diff --git a/packages/gitbook/src/intl/translations/lv.ts b/packages/gitbook/src/intl/translations/lv.ts index 397d78fb1..96c391e1a 100644 --- a/packages/gitbook/src/intl/translations/lv.ts +++ b/packages/gitbook/src/intl/translations/lv.ts @@ -11,6 +11,8 @@ export const lv: TranslationLanguage = { switch_to_system_theme: 'Pārslēgt uz sistēmas motīvu', search: 'Meklēt', clear: 'Notīrīt', + table_filtered_by_selection: 'Atbilst izvēlei: ${1}', + table_clear_selection: 'Notīrīt izvēli', tags: 'Birkas', search_back: 'Atpakaļ uz meklēšanas rezultātiem', search_or_ask: 'Jautāt vai meklēt', diff --git a/packages/gitbook/src/intl/translations/ms.ts b/packages/gitbook/src/intl/translations/ms.ts index 8ffd32b60..fe47cefdf 100644 --- a/packages/gitbook/src/intl/translations/ms.ts +++ b/packages/gitbook/src/intl/translations/ms.ts @@ -11,6 +11,8 @@ export const ms: TranslationLanguage = { switch_to_system_theme: 'Tukar kepada tema sistem', search: 'Cari', clear: 'Kosongkan', + table_filtered_by_selection: 'Sepadan dengan pilihan: ${1}', + table_clear_selection: 'Kosongkan pilihan', tags: 'Tag', search_back: 'Kembali ke hasil carian', search_or_ask: 'Tanya atau cari', diff --git a/packages/gitbook/src/intl/translations/nl.ts b/packages/gitbook/src/intl/translations/nl.ts index e2b5429f8..c7c231fdf 100644 --- a/packages/gitbook/src/intl/translations/nl.ts +++ b/packages/gitbook/src/intl/translations/nl.ts @@ -11,6 +11,8 @@ export const nl: TranslationLanguage = { switch_to_system_theme: 'Schakel over naar systeemmodus', search: 'Zoeken', clear: 'Wissen', + table_filtered_by_selection: 'Komt overeen met selectie: ${1}', + table_clear_selection: 'Selectie wissen', tags: 'Tags', search_back: 'Terug naar zoekresultaten', search_or_ask: 'Zoek of vraag', diff --git a/packages/gitbook/src/intl/translations/no.ts b/packages/gitbook/src/intl/translations/no.ts index d99465232..97e9babfc 100644 --- a/packages/gitbook/src/intl/translations/no.ts +++ b/packages/gitbook/src/intl/translations/no.ts @@ -11,6 +11,8 @@ export const no: TranslationLanguage = { switch_to_system_theme: 'Bytt til systemtema', search: 'Søk', clear: 'Tøm', + table_filtered_by_selection: 'Samsvarer med valg: ${1}', + table_clear_selection: 'Fjern valg', tags: 'Tagger', search_back: 'Tilbake til søkeresultater', search_or_ask: 'Spør eller søk', diff --git a/packages/gitbook/src/intl/translations/pl.ts b/packages/gitbook/src/intl/translations/pl.ts index bb94abf11..c667a9a9d 100644 --- a/packages/gitbook/src/intl/translations/pl.ts +++ b/packages/gitbook/src/intl/translations/pl.ts @@ -11,6 +11,8 @@ export const pl: TranslationLanguage = { switch_to_system_theme: 'Przełącz na motyw systemowy', search: 'Szukaj', clear: 'Wyczyść', + table_filtered_by_selection: 'Zgodne z wyborem: ${1}', + table_clear_selection: 'Wyczyść wybór', tags: 'Tagi', search_back: 'Wróć do wyników wyszukiwania', search_or_ask: 'Zapytaj lub wyszukaj', diff --git a/packages/gitbook/src/intl/translations/pt-br.ts b/packages/gitbook/src/intl/translations/pt-br.ts index fb4e10339..388087e12 100644 --- a/packages/gitbook/src/intl/translations/pt-br.ts +++ b/packages/gitbook/src/intl/translations/pt-br.ts @@ -11,6 +11,8 @@ export const pt_br: TranslationLanguage = { switch_to_system_theme: 'Mudar para configuração do sistema', search: 'Buscar', clear: 'Limpar', + table_filtered_by_selection: 'Corresponde à seleção: ${1}', + table_clear_selection: 'Limpar seleção', tags: 'Tags', search_back: 'Voltar aos resultados da busca', search_or_ask: 'Perguntar ou buscar', diff --git a/packages/gitbook/src/intl/translations/pt.ts b/packages/gitbook/src/intl/translations/pt.ts index 22ce39c8d..393378be4 100644 --- a/packages/gitbook/src/intl/translations/pt.ts +++ b/packages/gitbook/src/intl/translations/pt.ts @@ -11,6 +11,8 @@ export const pt: TranslationLanguage = { switch_to_system_theme: 'Mudar para o tema do sistema', search: 'Pesquisar', clear: 'Limpar', + table_filtered_by_selection: 'Corresponde à seleção: ${1}', + table_clear_selection: 'Limpar seleção', tags: 'Etiquetas', search_back: 'Voltar aos resultados da pesquisa', search_or_ask: 'Perguntar ou pesquisar', diff --git a/packages/gitbook/src/intl/translations/ro.ts b/packages/gitbook/src/intl/translations/ro.ts index aedc65e8b..faf1d45b5 100644 --- a/packages/gitbook/src/intl/translations/ro.ts +++ b/packages/gitbook/src/intl/translations/ro.ts @@ -11,6 +11,8 @@ export const ro: TranslationLanguage = { switch_to_system_theme: 'Comută la tema sistemului', search: 'Caută', clear: 'Șterge', + table_filtered_by_selection: 'Se potrivește cu selecția: ${1}', + table_clear_selection: 'Șterge selecția', tags: 'Etichete', search_back: 'Înapoi la rezultatele căutării', search_or_ask: 'Întreabă sau caută', diff --git a/packages/gitbook/src/intl/translations/ru.ts b/packages/gitbook/src/intl/translations/ru.ts index 1bc0bf505..21a470236 100644 --- a/packages/gitbook/src/intl/translations/ru.ts +++ b/packages/gitbook/src/intl/translations/ru.ts @@ -11,6 +11,8 @@ export const ru: TranslationLanguage = { switch_to_system_theme: 'Переключиться на системную тему', search: 'Поиск', clear: 'Очистить', + table_filtered_by_selection: 'Соответствует выбору: ${1}', + table_clear_selection: 'Очистить выбор', tags: 'Теги', search_back: 'Вернуться к результатам поиска', search_or_ask: 'Найти или спросить', diff --git a/packages/gitbook/src/intl/translations/sk.ts b/packages/gitbook/src/intl/translations/sk.ts index 8a9cd2fba..c4824bfe7 100644 --- a/packages/gitbook/src/intl/translations/sk.ts +++ b/packages/gitbook/src/intl/translations/sk.ts @@ -11,6 +11,8 @@ export const sk: TranslationLanguage = { switch_to_system_theme: 'Prepnúť na systémový motív', search: 'Hľadať', clear: 'Vymazať', + table_filtered_by_selection: 'Zodpovedá výberu: ${1}', + table_clear_selection: 'Zrušiť výber', tags: 'Značky', search_back: 'Späť na výsledky vyhľadávania', search_or_ask: 'Opýtať sa alebo hľadať', diff --git a/packages/gitbook/src/intl/translations/sl.ts b/packages/gitbook/src/intl/translations/sl.ts index 547177548..658ee2c38 100644 --- a/packages/gitbook/src/intl/translations/sl.ts +++ b/packages/gitbook/src/intl/translations/sl.ts @@ -11,6 +11,8 @@ export const sl: TranslationLanguage = { switch_to_system_theme: 'Preklopi na sistemsko temo', search: 'Išči', clear: 'Počisti', + table_filtered_by_selection: 'Ustreza izbiri: ${1}', + table_clear_selection: 'Počisti izbiro', tags: 'Oznake', search_back: 'Nazaj na rezultate iskanja', search_or_ask: 'Vprašaj ali išči', diff --git a/packages/gitbook/src/intl/translations/sv.ts b/packages/gitbook/src/intl/translations/sv.ts index 4370a4ec0..efe4409a4 100644 --- a/packages/gitbook/src/intl/translations/sv.ts +++ b/packages/gitbook/src/intl/translations/sv.ts @@ -11,6 +11,8 @@ export const sv: TranslationLanguage = { switch_to_system_theme: 'Byt till systemtema', search: 'Sök', clear: 'Rensa', + table_filtered_by_selection: 'Matchar val: ${1}', + table_clear_selection: 'Rensa val', tags: 'Taggar', search_back: 'Tillbaka till sökresultat', search_or_ask: 'Fråga eller sök', diff --git a/packages/gitbook/src/intl/translations/th.ts b/packages/gitbook/src/intl/translations/th.ts index 18495ebc3..c26e87cc8 100644 --- a/packages/gitbook/src/intl/translations/th.ts +++ b/packages/gitbook/src/intl/translations/th.ts @@ -11,6 +11,8 @@ export const th: TranslationLanguage = { switch_to_system_theme: 'เปลี่ยนเป็นธีมของระบบ', search: 'ค้นหา', clear: 'ล้าง', + table_filtered_by_selection: 'ตรงกับการเลือก: ${1}', + table_clear_selection: 'ล้างการเลือก', tags: 'แท็ก', search_back: 'กลับไปยังผลการค้นหา', search_or_ask: 'ถามหรือค้นหา', diff --git a/packages/gitbook/src/intl/translations/tr.ts b/packages/gitbook/src/intl/translations/tr.ts index ec9db6c66..dd67763f9 100644 --- a/packages/gitbook/src/intl/translations/tr.ts +++ b/packages/gitbook/src/intl/translations/tr.ts @@ -11,6 +11,8 @@ export const tr: TranslationLanguage = { switch_to_system_theme: 'Sistem temasına geç', search: 'Ara', clear: 'Temizle', + table_filtered_by_selection: 'Seçimle eşleşiyor: ${1}', + table_clear_selection: 'Seçimi temizle', tags: 'Etiketler', search_back: 'Arama sonuçlarına geri dön', search_or_ask: 'Sor veya ara', diff --git a/packages/gitbook/src/intl/translations/uk.ts b/packages/gitbook/src/intl/translations/uk.ts index 2be479571..85966ed2d 100644 --- a/packages/gitbook/src/intl/translations/uk.ts +++ b/packages/gitbook/src/intl/translations/uk.ts @@ -11,6 +11,8 @@ export const uk: TranslationLanguage = { switch_to_system_theme: 'Перемкнути на системну тему', search: 'Пошук', clear: 'Очистити', + table_filtered_by_selection: 'Відповідає вибору: ${1}', + table_clear_selection: 'Очистити вибір', tags: 'Теги', search_back: 'Назад до результатів пошуку', search_or_ask: 'Запитати або шукати', diff --git a/packages/gitbook/src/intl/translations/vi.ts b/packages/gitbook/src/intl/translations/vi.ts index 83b498bb4..c1c57e05f 100644 --- a/packages/gitbook/src/intl/translations/vi.ts +++ b/packages/gitbook/src/intl/translations/vi.ts @@ -11,6 +11,8 @@ export const vi: TranslationLanguage = { switch_to_system_theme: 'Chuyển sang giao diện hệ thống', search: 'Tìm kiếm', clear: 'Xóa', + table_filtered_by_selection: 'Khớp với lựa chọn: ${1}', + table_clear_selection: 'Xóa lựa chọn', tags: 'Thẻ', search_back: 'Quay lại kết quả tìm kiếm', search_or_ask: 'Hỏi hoặc tìm kiếm', diff --git a/packages/gitbook/src/intl/translations/yue.ts b/packages/gitbook/src/intl/translations/yue.ts index f6952985e..93450ca4f 100644 --- a/packages/gitbook/src/intl/translations/yue.ts +++ b/packages/gitbook/src/intl/translations/yue.ts @@ -11,6 +11,8 @@ export const yue: TranslationLanguage = { switch_to_system_theme: '切換到系統主題', search: '搜尋', clear: '清除', + table_filtered_by_selection: '符合選擇:${1}', + table_clear_selection: '清除選擇', tags: '標籤', search_back: '返回搜尋結果', search_or_ask: '發問或搜尋', diff --git a/packages/gitbook/src/intl/translations/zh-tw.ts b/packages/gitbook/src/intl/translations/zh-tw.ts index 6cb490a09..5141e5ff9 100644 --- a/packages/gitbook/src/intl/translations/zh-tw.ts +++ b/packages/gitbook/src/intl/translations/zh-tw.ts @@ -11,6 +11,8 @@ export const zh_tw: TranslationLanguage = { switch_to_system_theme: '切換至系統主題', search: '搜尋', clear: '清除', + table_filtered_by_selection: '符合選擇:${1}', + table_clear_selection: '清除選擇', tags: '標籤', search_back: '返回搜尋結果', search_or_ask: '詢問或搜尋', diff --git a/packages/gitbook/src/intl/translations/zh.ts b/packages/gitbook/src/intl/translations/zh.ts index 34808b42c..552613251 100644 --- a/packages/gitbook/src/intl/translations/zh.ts +++ b/packages/gitbook/src/intl/translations/zh.ts @@ -11,6 +11,8 @@ export const zh: TranslationLanguage = { switch_to_system_theme: '切换到系统主题', search: '搜索', clear: '清除', + table_filtered_by_selection: '符合选择:${1}', + table_clear_selection: '清除选择', tags: '标签', search_back: '返回搜索结果', search_or_ask: '询问或搜索',