mirror of
https://github.com/supabase/supabase.git
synced 2026-05-08 15:57:47 +08:00
## What kind of change does this PR introduce? - Resolves FE-1960 - Resolves FE-1983 - Resolves DEPR-207 ## What is the current behavior? Page titles between surfaces are inconsistent and vague. Sometimes they say the product name: ``` My Project | My Org | Supabase ``` ...even when on a specific surface like Database > Tables. Other times they show the entity name but skip over the project or org name : ``` Edge Functions | Supabase ``` ## What is the new behavior? Page titles *mostly* (see below) follow the same format: ``` users | Table Editor | My Project | My Org | Supabase hello-world | Logs | Edge Functions | My Project | My Org | Supabase Backups | Database | My Project | My Org | Supabase Authentication | My Project | My Org | Supabase ``` That format is: entity, section, surface, project, org, brand ## Additional context This is stacked PR 1/5 for page title improvements. Includes the core title utility and ProjectLayout integration/tests. Follow-up stacked PRs are based on this branch: - https://github.com/supabase/supabase/pull/43534 - https://github.com/supabase/supabase/pull/43535 - https://github.com/supabase/supabase/pull/43536 - https://github.com/supabase/supabase/pull/43537 This one should be merged first. The others (listed right above) can _then_ be merged in any order.
47 lines
1.1 KiB
TypeScript
47 lines
1.1 KiB
TypeScript
export interface StudioPageTitleParts {
|
|
entity?: string
|
|
section?: string
|
|
surface?: string
|
|
project?: string
|
|
org?: string
|
|
brand?: string
|
|
}
|
|
|
|
export const STUDIO_PAGE_TITLE_SEPARATOR = ' | '
|
|
const MAX_SEGMENT_LENGTH = 60
|
|
|
|
const normalizeTitleSegment = (value?: string) => {
|
|
if (value === undefined) return undefined
|
|
|
|
const normalized = value.trim().replace(/\s+/g, ' ')
|
|
if (normalized.length === 0) return undefined
|
|
|
|
if (normalized.length <= MAX_SEGMENT_LENGTH) return normalized
|
|
return `${normalized.slice(0, MAX_SEGMENT_LENGTH - 1).trimEnd()}…`
|
|
}
|
|
|
|
export const buildStudioPageTitle = (parts: StudioPageTitleParts) => {
|
|
const orderedParts = [
|
|
parts.entity,
|
|
parts.section,
|
|
parts.surface,
|
|
parts.project,
|
|
parts.org,
|
|
parts.brand,
|
|
]
|
|
|
|
const segments: string[] = []
|
|
|
|
orderedParts.forEach((part) => {
|
|
const segment = normalizeTitleSegment(part)
|
|
if (!segment) return
|
|
|
|
const lastSegment = segments[segments.length - 1]
|
|
if (lastSegment !== undefined && lastSegment.toLowerCase() === segment.toLowerCase()) return
|
|
|
|
segments.push(segment)
|
|
})
|
|
|
|
return segments.join(STUDIO_PAGE_TITLE_SEPARATOR)
|
|
}
|