Skip to content

Commit

Permalink
feat: Project breadcrumb links. (#211)
Browse files Browse the repository at this point in the history
Ability for the api to return a list of breadcrumb links for the UI to use.

This allows the UI to link to different levels of the project (ex: link to the onboarding flow) easily.
  • Loading branch information
Zoramite committed Aug 6, 2021
1 parent 49ef330 commit 9f8a07d
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 14 deletions.
13 changes: 13 additions & 0 deletions src/sass/parts/_overview.sass
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,16 @@

path
fill: currentColor

.le__part__breadcrumb
overflow: hidden
text-overflow: ellipsis
white-space: nowrap

span
&::after
content: ' / '

&:last-child
&::after
content: ''
34 changes: 33 additions & 1 deletion src/ts/editor/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ export interface LiveEditorApiComponent {
/**
* Retrieve the url to preview the file.
*
* When retrieving a list of files it is often slow
* When retrieving a list of files it is often slow to
* retrieve the serving url for all the files. If the
* preview url is missing, the API will be called to
* the serving url on a per file basis.
*/
getFileUrl(file: FileData): Promise<FileData>;

Expand Down Expand Up @@ -549,6 +552,10 @@ export interface ProjectData {
* specific features of the editor.
*/
features?: Record<string, boolean | FeatureManagerSettings>;
/**
* The editor will use important links for the project in the UI.
*/
links?: ProjectLinksData;
/**
* Media configuration for the project.
*
Expand Down Expand Up @@ -591,6 +598,31 @@ export interface ProjectData {
users?: Array<UserData>;
}

/**
* Links for the project that can be used in the UI to direct the user.
*/
export interface ProjectLinksData {
/**
* The UI can display a breadcrumb style heirarchy links for the project.
* This allows for easier navigation between projects on a given service.
*/
breadcrumbs?: Array<LinkData>;
}

/**
* Information about a specific link.
*/
export interface LinkData {
/**
* Label for how the link is displayed.
*/
label?: string;
/**
* URL for the link.
*/
url: string;
}

/**
* Result from pinging the api.
*/
Expand Down
9 changes: 1 addition & 8 deletions src/ts/editor/ui/parts/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,16 +203,9 @@ export class MenuPart extends BasePart implements UiPartComponent {
}

templateMenu(): TemplateResult {
const project = this.config.state.project;

// Lazy load the project.
if (!project) {
this.loadProject();
}

return html`<div class="le__part__menu__header">
<div class="le__part__menu__project">
${project?.title || html`&nbsp;`}
${this.config.editor.ui.partOverview.templateProjectTitle()}
</div>
<div class="le__actions">
${this.templateActionDocking()} ${this.templateActionClose()}
Expand Down
39 changes: 34 additions & 5 deletions src/ts/editor/ui/parts/overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
classMap,
html,
ifDefined,
repeat,
} from '@blinkk/selective-edit';
import {DialogActionLevel, FormDialogModal} from '../modal';
import {exampleIcon, githubIcon, localIcon} from '../icons';
Expand Down Expand Up @@ -226,21 +227,49 @@ export class OverviewPart extends BasePart implements UiPartComponent {
}

templateProject(): TemplateResult {
return html`<div class="le__part__overview__title">
${this.config.editor.ui.partMenu.isDocked
? html`&nbsp;`
: this.templateProjectTitle()}
</div>`;
}

templateProjectTitle(): TemplateResult {
const project = this.config.state.project;

// Lazy load the project.
if (!project) {
if (project === undefined) {
this.loadProject();
}

let projectName = project?.title || html`&nbsp;`;
const parts: Array<TemplateResult> = [];
let links = project?.links?.breadcrumbs ?? [];

// TODO: Better way of limiting the breadcrumbs for services.
if (links.length > 2) {
// Trim off the service level and branch level links.
links = links.slice(1, -1);
}

// Menu shows the project name when it is docked.
if (this.config.editor.ui.partMenu.isDocked) {
projectName = html`&nbsp;`;
if (links.length) {
parts.push(
html`${repeat(
links,
link => link.url,
link =>
html`<span
><a href=${link.url} alt=${ifDefined(link.label)}
>${link.label}</a
></span
>`
)}`
);
} else {
parts.push(html`${project?.title || html`&nbsp;`}`);
}

return html`<div class="le__part__overview__title">${projectName}</div>`;
return html`<div class="le__part__breadcrumb">${parts}</div>`;
}

templatePublish(): TemplateResult {
Expand Down
48 changes: 48 additions & 0 deletions src/ts/server/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,50 @@ export class ServiceServerApi extends ServerApi {
};
}

async getProject(): Promise<ProjectData> {
const response = await super.getProject();

// Generate breadcrumb links for the project.
const links = response.links ?? {};
const breadcrumbs = links.breadcrumbs ?? [];

// Only generate the breadcrumbs here if the
// remote api does not provide them.
if (breadcrumbs.length === 0) {
// Main service breadcrumb.
breadcrumbs.push({
label: this.serviceName,
url: `/${this.service}/`,
});

if (this.organization) {
breadcrumbs.push({
label: this.organization,
url: `/${this.service}/${this.organization}/`,
});

if (this.project) {
breadcrumbs.push({
label: this.project,
url: `/${this.service}/${this.organization}/${this.project}/`,
});

if (this.branch) {
breadcrumbs.push({
label: this.branch,
url: `/${this.service}/${this.organization}/${this.project}/${this.branch}/`,
});
}
}
}
}

links.breadcrumbs = breadcrumbs;
response.links = links;

return response;
}

protected get onboardingStatus(): OnboardingStatus {
// Only valid when all required information is set.
if (
Expand All @@ -481,6 +525,10 @@ export class ServiceServerApi extends ServerApi {
return OnboardingStatus.Missing;
}

get serviceName(): string {
return 'Service';
}

async updateOnboarding(info: OnboardingInfo): Promise<OnboardingInfo> {
if (info.data) {
const infoData: OnboardingDataGitHub = info.data as OnboardingDataGitHub;
Expand Down
4 changes: 4 additions & 0 deletions src/ts/server/gh/githubApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ export class GitHubApi extends ServiceServerApi {
) as Promise<Array<WorkspaceData>>;
}

get serviceName(): string {
return 'GitHub';
}

/**
* Start the authentication process.
*/
Expand Down

0 comments on commit 9f8a07d

Please sign in to comment.