feature: implement tabs render template

This commit is contained in:
Sagi 2022-09-30 14:05:39 +08:00
parent 556b8d5594
commit 1aea7f3057
3 changed files with 335 additions and 105 deletions

View File

@ -1,110 +1,112 @@
const types = ['config', 'feature', 'fix', 'docs', 'style', 'refactor', 'performance', 'test', 'build', 'release', 'chore', 'revert'];
module.exports = {
parserPreset: { parserOpts: { headerPattern: /^(\w*)(?:\((.*)\))?!?: (.*)$/ } },
extends: ['@commitlint/config-conventional'],
rules: {
'type-empty': [2, 'never'],
'type-enum': [2, 'always', types],
'scope-case': [0, 'always'],
'subject-empty': [2, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [2, 'always', 88],
},
prompt: {
questions: {
type: {
description: "Select the type of change that you're committing",
enum: {
config: {
description: 'Changes that affect the tools, such as eslint, npm, vscode.',
title: 'Config',
emoji: '🛠',
},
feature: {
description: 'A new feature',
title: 'Features',
emoji: '✨',
},
fix: {
description: 'A bug fix',
title: 'Bug Fixes',
emoji: '🐛',
},
docs: {
description: 'Documentation only changes',
title: 'Documentation',
emoji: '📚',
},
style: {
description: 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)',
title: 'Styles',
emoji: '💎',
},
refactor: {
description: 'A code change that neither fixes a bug nor adds a feature',
title: 'Code Refactoring',
emoji: '📦',
},
performance: {
description: 'A code change that improves performance',
title: 'Performance Improvements',
emoji: '🚀',
},
test: {
description: 'Adding missing tests or correcting existing tests',
title: 'Tests',
emoji: '🚨',
},
build: {
description: 'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)',
title: 'Builds',
emoji: '🛠',
},
ci: {
description: 'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)',
title: 'Continuous Integrations',
emoji: '⚙️',
},
chore: {
description: "Other changes that don't modify src or test files",
title: 'Chores',
emoji: '♻️',
},
revert: {
description: 'Reverts a previous commit',
title: 'Reverts',
emoji: '🗑',
},
},
},
scope: {
description: 'What is the scope of this change (e.g. component or file name)',
},
subject: {
description: 'Write a short, imperative tense description of the change',
},
body: {
description: 'Provide a longer description of the change',
},
isBreaking: {
description: 'Are there any breaking changes?',
},
breakingBody: {
description: 'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself',
},
breaking: {
description: 'Describe the breaking changes',
},
isIssueAffected: {
description: 'Does this change affect any open issues?',
},
issuesBody: {
description: 'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself',
},
issues: {
description: 'Add issue references (e.g. "fix #123", "re #123".)',
},
parserPreset: { parserOpts: { headerPattern: /^(\w*)(?:\((.*)\))?!?: (.*)$/ } },
extends: ['@commitlint/config-conventional'],
rules: {
'type-empty': [2, 'never'],
'type-enum': [2, 'always', types],
'scope-case': [0, 'always'],
'subject-empty': [2, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [2, 'always', 88],
},
prompt: {
questions: {
type: {
description: "Select the type of change that you're committing",
enum: {
config: {
description: 'Changes that affect the tools, such as eslint, npm, vscode.',
title: 'Config',
emoji: '🛠',
},
feature: {
description: 'A new feature',
title: 'Features',
emoji: '✨',
},
fix: {
description: 'A bug fix',
title: 'Bug Fixes',
emoji: '🐛',
},
docs: {
description: 'Documentation only changes',
title: 'Documentation',
emoji: '📚',
},
style: {
description:
'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)',
title: 'Styles',
emoji: '💎',
},
refactor: {
description: 'A code change that neither fixes a bug nor adds a feature',
title: 'Code Refactoring',
emoji: '📦',
},
performance: {
description: 'A code change that improves performance',
title: 'Performance Improvements',
emoji: '🚀',
},
test: {
description: 'Adding missing tests or correcting existing tests',
title: 'Tests',
emoji: '🚨',
},
build: {
description: 'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)',
title: 'Builds',
emoji: '🛠',
},
ci: {
description:
'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)',
title: 'Continuous Integrations',
emoji: '⚙️',
},
chore: {
description: "Other changes that don't modify src or test files",
title: 'Chores',
emoji: '♻️',
},
revert: {
description: 'Reverts a previous commit',
title: 'Reverts',
emoji: '🗑',
},
},
},
scope: {
description: 'What is the scope of this change (e.g. component or file name)',
},
subject: {
description: 'Write a short, imperative tense description of the change',
},
body: {
description: 'Provide a longer description of the change',
},
isBreaking: {
description: 'Are there any breaking changes?',
},
breakingBody: {
description: 'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself',
},
breaking: {
description: 'Describe the breaking changes',
},
isIssueAffected: {
description: 'Does this change affect any open issues?',
},
issuesBody: {
description: 'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself',
},
issues: {
description: 'Add issue references (e.g. "fix #123", "re #123".)',
},
},
},
}
};

View File

@ -0,0 +1,97 @@
<div #tabHeader class="farris-tabs-header"
[class.farris-tabs-inHead]="hasInHeadCl()"
[class.farris-tabs-inContent]="!hasInHeadCl()"
[class.farris-tabs-nav-fill]="fill||tabType=='fill'"
[class.farris-tabs-nav-pills]="tabType=='pills'"
>
<div class="farris-tabs-title scroll-tabs"
[ngStyle]="{'width': hasInHeadCl() ? (titleWidth?titleWidth + '%':null) : null}">
<button type="button" (disabled)="disableLeft" class="btn sc-nav-btn px-1 sc-nav-lr"
[ngClass]="{'d-none':hideButtons}" #leftNav>
</button>
<div class="spacer f-utils-fill" #tabParent
[ngClass]="{'spacer-sides':!hideButtons&& hideDropDown,'spacer-sides-dropdown':!hideButtons&&!hideDropDown}"
style="width:100%;">
<ul class="nav farris-nav-tabs flex-nowrap" [class.nav-fill]="fill||tabType=='fill'"
[class.flex-row]="position==='top'||position==='bottom'"
[class.flex-column]="position==='left'||position==='right'" [class.nav-pills]="tabType=='pills'" #tabContainer>
<li class="nav-item" *ngFor="let tab of tabs;index as tabIndex"
[ngClass]="{'d-none': tab._show !== true, 'f-state-active': tab.id === activeId, 'f-state-disabled': tab.disabled === true}"
[ngStyle]="{'width':tab.tabWidth+'px'}">
<a class="nav-link tabs-text-truncate" #anchor [class.active]="tab.id === activeId"
[class.disabled]="tab.disabled" (click)="_cpSelectTabByIndex($event,tab.id)">
<span class="st-tab-text" [class.farris-title-auto]="autoTitleWidth"
[ngClass]="tab.titleCustomCls" [tabTransclude]="tab.headingRef"
*ngIf="tab.headingRef"></span>
<span class="st-tab-text" [ngbTooltip]="tab.title" [referElement]="tabContainer"
[disableTooltip]="!tab.titleOverflow" [class.farris-title-auto]="autoTitleWidth"
[class.farris-title-text-custom]="tab.titleOverflow"
*ngIf="!tab.headingRef">{{tab.title}}</span>
<span class="st-drop-close" *ngIf="tab.removeable">
<i class="f-icon f-icon-close"></i>
</span>
</a>
</li>
</ul>
</div>
<div class="btn-group sc-nav-btn" ngbDropdown (openChange)="openChange($event)" >
<button type="button" (disabled)="disableRight" class="btn sc-nav-rg" [ngClass]="{'d-none':hideButtons}"
#rightNav></button>
<button #dropDownButton class="btn dropdown-toggle-split " ngbDropdownToggle
[ngClass]="{'d-none':hideDropDown}">
</button>
<div class="dropdown-menu" [class.tabs-pt28]="searchBoxVisible" ngbDropdownMenu #ddm="DropdownMenu">
<div (click)="stopBubble($event)" class='pb-1 tabs-li-absolute' *ngIf="searchBoxVisible">
<input type="text" class="form-control k-textbox" [(ngModel)]="searchTabText"
(keyup)="searchTab($event)" />
<span class="f-icon f-icon-page-title-query tabs-icon-search"></span>
</div>
<ul class="tab-dropdown-menu--items" [ngStyle]="{'width':menuItemsWidth}">
<li class="dropdown-item text-truncate px-2" *ngFor="let tab of dropdownTabs;index as tabIndex"
[ngClass]="{'disabled': tab.disabled, 'active': tab.id === activeId, 'd-none': tab._show !==true}"
(click)="_cpSelectTabByIndex($event,tab.id, ddm)">
<!-- <span class="float-right st-drop-close">
<i class="material-icons align-middle">close</i>
</span> -->
<a class="dropdown-title">{{tab.title}}</a>
</li>
</ul>
</div>
</div>
</div>
<div #toolbarContainer class="farris-tabs-toolbar" *ngIf="toolbarConf || headerExtendConf">
<div class="farris-tabs-inline-flex" *ngIf="toolbarConf">
<ng-template ngFor let-btn [ngForOf]="toolbarConf.contents" let-i="index" [ngForTrackBy]="trackByButton">
<button [attr.id]="btn.id" [ngClass]="btn.appearance.class"
*ngIf="getToolbarState(btn.id, btnVisible) | async"
[disabled]="getToolbarState(btn.id, btnStates) | async"
(click)="clickHandler(btn)">{{btn.title}}</button>
</ng-template>
</div>
<div class="btn-group sc-nav-btn" ngbDropdown [hidden]="toolbarDpHidden">
<button class="btn btn-link morebtn" ngbDropdownToggle>{{'tabs.more' | locale}}</button>
<ul class="dropdown-menu" ngbDropdownMenu *ngIf="inMoreButtonContents.length">
<ng-template ngFor let-content [ngForOf]="inMoreButtonContents" let-i="index">
<li class="dropdown-item text-truncate px-2"
*ngIf="getToolbarState(content.id, btnVisible) | async">
<button style="width:100%;" [attr.id]="content.id" class="btn btn-link text-left"
[class.disabled]="getToolbarState(content.id, btnStates) | async"
(click)="clickHandler(content)">{{content.title}}</button>
</li>
</ng-template>
</ul>
</div>
<div *ngIf="headerExtendConf && headerExtendConf.templateRef" class="farris-tabs-header-extend"
[class.margin-right-14]="toolbarConf && toolbarConf.contents.length"
[ngClass]="headerExtendConf && headerExtendConf.extendCls">
<template [ngTemplateOutlet]="headerExtendConf.templateRef"></template>
</div>
</div>
<div class="farris-tabs-btn-placeholder" #btnPlaceholder *ngIf="toolbarConf">
<ng-container *ngFor="let btn of toolbarConf.contents">
<button [ngClass]="btn.appearance.class"
*ngIf="getToolbarState(btn.id, btnVisible) | async">{{btn.title}}</button>
</ng-container>
</div>
</div>
<ng-content></ng-content>

View File

@ -0,0 +1,131 @@
import { computed, defineComponent, ref, SetupContext } from 'vue';
import { TabsProps, tabsProps } from './tabs.props';
export default defineComponent({
name: 'FTabs',
props: tabsProps,
emits: [],
setup(props: TabsProps, context: SetupContext) {
const hideButtons = ref(false);
const hideDropDown = ref(false);
const hasInHeadClass = computed(() => {
return false;
});
const fill = ref('');
const tabType = ref('');
const position = ref(props.position);
const tabs = ref([]);
const shouldShowNavFill = computed(() => {
fill.value || tabType.value === 'fill';
});
const shouldShowNavPills = computed(() => {});
const tabsHeaderClass = computed(() => ({
'farris-tabs-header': true,
'farris-tabs-inHead': hasInHeadClass.value,
'farris-tabs-inContent': !hasInHeadClass.value,
'farris-tabs-nav-fill': shouldShowNavFill.value,
'farris-tabs-nav-pills': shouldShowNavPills.value,
}));
const tabsTitleStyle = computed(() => ({
width: hasInHeadClass.value ? (props.titleWidth ? `${props.titleWidth}%` : '') : '',
}));
const tabsTitleButtonClass = computed(() => ({
btn: true,
'sc-nav-btn': true,
'px-1': true,
'sc-nav-lr': true,
'd-none': hideButtons.value,
}));
const tabParentClass = computed(() => ({
spacer: true,
'f-utils-fill': true,
'spacer-sides': !hideButtons.value && hideDropDown.value,
'spacer-sides-dropdown': !hideButtons.value && !hideDropDown.value,
}));
const tabContainerClass = computed(() => ({
nav: true,
'farris-nav-tabs': true,
'flex-nowrap': true,
'nav-fill': fill.value || tabType.value === 'fill',
'nav-pills': tabType.value === 'pills',
'flex-row': position.value === 'top' || position.value === 'bottom',
'flex-column': position.value === 'left' || position.value === 'right',
}));
function getTabClass(tab: any) {
return {
'nav-item': true,
'd-none': !tab.show,
'f-state-active': tab.id === activeId,
'f-state-disable': tab.disabled,
};
}
function getTabStyle(tab: any) {
return { width: `${tab.tabWidth}px` };
}
function getTabNavLinkClass(tab: any) {
return {
'nav-link': true,
'tabs-text-truncate': true,
active: tab.id === activeId,
disabled: tab.disabled,
};
}
function selectTabByIndex($event: Event, targetTabId: string) {}
function getTabTextClass(tab: any) {
return {
'st-tab-text': true,
'farris-title-auto': props.autoTitleWidth,
'farris-title-text-custom': tab.titleOverflow,
};
}
return () => {
return (
<>
<div class={tabsHeaderClass.value}>
<div class="farris-tabs-title scroll-tabs" style={tabsTitleStyle.value}>
<button type="button" class={tabsTitleButtonClass.value}></button>
<div class={tabParentClass.value} style="width:100%">
<ul class={tabContainerClass.value}>
{tabs.value.forEach((tab: any, tabIndex) => {
return (
<li class={getTabClass(tab)} style={getTabStyle(tab)}>
<a class={getTabNavLinkClass(tab)} onClick={($event) => selectTabByIndex($event, tab.id)}>
<span class={getTabTextClass(tab)}>{tab.title}</span>
{tab.removeable && (
<span class="st-drop-close">
<i class="f-icon f-icon-close"></i>
</span>
)}
</a>
</li>
);
})}
</ul>
</div>
</div>
</div>
{context.slots.default && context.slots.default()}
</>
);
};
},
});