@ribajs/bs4
Advanced tools
Comparing version 1.7.0 to 1.8.0
@@ -1,14 +0,12 @@ | ||
// iMPORTANT do not use .babelrc: https://github.com/babel/babel/issues/8711#issuecomment-421918023 | ||
module.exports = { | ||
"presets": [ | ||
"@babel/typescript", | ||
[ | ||
"@babel/typescript", { | ||
"allExtensions": true | ||
} | ||
], | ||
[ | ||
"@babel/preset-env", { | ||
"targets": { | ||
"ie": "11", | ||
"safari": "10", | ||
"chrome": "52", | ||
"edge": "16", | ||
"firefox": "59" | ||
} | ||
corejs: 3, | ||
useBuiltIns: "entry", // or "usage" | ||
} | ||
@@ -20,9 +18,11 @@ ] | ||
"@babel/plugin-transform-runtime", { | ||
"corejs": 2 | ||
"corejs": 3 | ||
} | ||
], | ||
"@babel/plugin-syntax-export-default-from", | ||
"@babel/plugin-proposal-class-properties", | ||
"@babel/plugin-proposal-object-rest-spread", | ||
"@babel/plugin-proposal-optional-chaining", | ||
"array-includes" | ||
] | ||
}; | ||
}; |
module.exports = { | ||
"testEnvironment": "jsdom", | ||
"moduleFileExtensions": [ | ||
testEnvironment: "jsdom", | ||
moduleFileExtensions: [ | ||
"ts", | ||
@@ -9,7 +9,7 @@ "tsx", | ||
], | ||
"transform": { | ||
transform: { | ||
"^.+\\.tsx?$": "ts-jest" | ||
}, | ||
"testRegex": "/src/.*\\.(test|spec).(ts|tsx)$", | ||
"collectCoverageFrom": [ | ||
testRegex: "/src/.*\\.(test|spec).(ts|tsx)$", | ||
collectCoverageFrom: [ | ||
"src/**/*.{tsx,ts}", | ||
@@ -19,6 +19,12 @@ "!**/node_modules/**", | ||
], | ||
"coverageReporters": [ | ||
coverageReporters: [ | ||
"json", | ||
"lcov" | ||
], | ||
], | ||
setupFilesAfterEnv: ["jest-extended"], | ||
globals: { | ||
'ts-jest': { | ||
babelConfig: true | ||
} | ||
} | ||
}; |
{ | ||
"name": "@ribajs/bs4", | ||
"description": "Bootstrap 4 module for Riba.js", | ||
"version": "1.7.0", | ||
"version": "1.8.0", | ||
"author": "Pascal Garber <pascal@artandcode.studio>", | ||
@@ -37,28 +37,38 @@ "contributors": [], | ||
"scripts": { | ||
"test": "npm run type-check && jest --config=jest.config.js", | ||
"type-check": "tslint -c tslint.json 'src/**/*.ts' && tsc --noEmit", | ||
"build": "npm run build:module", | ||
"build:module": "tsc" | ||
"test": "npm run clean && npm run type-check && jest --config=jest.config.js", | ||
"type-check": "eslint src --ext .js,.jsx,.ts,.tsx --fix && tsc --noEmit", | ||
"build": "npm run type-check && npm run build:module", | ||
"build:module": "tsc", | ||
"clean": "rm -rf ./dist ./lib", | ||
"packages:link": "npm link @ribajs/core @ribajs/extras" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "^7.7.4", | ||
"@babel/core": "^7.7.4", | ||
"@babel/cli": "^7.7.7", | ||
"@babel/core": "^7.7.7", | ||
"@babel/plugin-proposal-class-properties": "^7.7.4", | ||
"@babel/plugin-proposal-object-rest-spread": "^7.7.4", | ||
"@babel/plugin-transform-runtime": "^7.7.4", | ||
"@babel/preset-env": "^7.7.4", | ||
"@babel/preset-typescript": "^7.7.4", | ||
"@types/jest": "^24.0.23", | ||
"@babel/plugin-proposal-object-rest-spread": "^7.7.7", | ||
"@babel/plugin-proposal-optional-chaining": "^7.7.5", | ||
"@babel/plugin-syntax-export-default-from": "^7.7.4", | ||
"@babel/plugin-transform-runtime": "^7.7.6", | ||
"@babel/preset-env": "^7.7.7", | ||
"@babel/preset-typescript": "^7.7.7", | ||
"@babel/runtime-corejs3": "^7.7.7", | ||
"@types/jest": "^24.0.25", | ||
"@typescript-eslint/eslint-plugin": "^2.15.0", | ||
"@typescript-eslint/parser": "^2.15.0", | ||
"babel-loader": "^8.0.6", | ||
"babel-plugin-array-includes": "^2.0.3", | ||
"core-js": "^3.6.2", | ||
"eslint": "^6.8.0", | ||
"jest": "^24.9.0", | ||
"ts-jest": "^24.2.0", | ||
"tslint": "^5.20.1", | ||
"typescript": "^3.7.2", | ||
"webpack": "^4.41.2", | ||
"jest-extended": "^0.11.2", | ||
"ts-jest": "^24.3.0", | ||
"typescript": "^3.7.4", | ||
"webpack": "^4.41.5", | ||
"webpack-cli": "^3.3.10" | ||
}, | ||
"dependencies": { | ||
"@ribajs/core": "^1.7.0", | ||
"bootstrap": "^4.3.1", | ||
"@ribajs/core": "^1.8.0", | ||
"@ribajs/extras": "^1.8.0", | ||
"bootstrap": "^4.4.1", | ||
"popper.js": "^1.16.0" | ||
@@ -65,0 +75,0 @@ }, |
@@ -12,3 +12,3 @@ import { Binder } from '@ribajs/core'; | ||
const targets = el.querySelectorAll(targetSelector); | ||
const targets = el.querySelectorAll<HTMLElement>(targetSelector); | ||
@@ -15,0 +15,0 @@ const collapseService = new CollapseService(targets); |
@@ -10,3 +10,3 @@ import { Binder } from '@ribajs/core'; | ||
name: 'bs4-dropdown', | ||
routine(el: HTMLElement, option: string) { | ||
routine(el: HTMLElement, option: any = {}) { | ||
let toggler: HTMLButtonElement; | ||
@@ -23,5 +23,5 @@ if (el.classList.contains('dropdown-toggle')) { | ||
const dropdownService = new DropdownService(toggler); | ||
const dropdownService = new DropdownService(toggler, option); | ||
toggler.addEventListener('click', (event) => { | ||
toggler.addEventListener('click', () => { | ||
dropdownService.toggle(); | ||
@@ -28,0 +28,0 @@ }); |
@@ -1,2 +0,2 @@ | ||
import { Binder } from '@ribajs/core'; | ||
import { Binder, Utils } from '@ribajs/core'; | ||
@@ -56,3 +56,3 @@ /** | ||
}; | ||
window.addEventListener('scroll', this.customData.onScroll); | ||
window.addEventListener('scroll', Utils.debounce(this.customData.onScroll.bind(this)), { passive: true }); | ||
this.customData.onScroll(); | ||
@@ -66,4 +66,4 @@ }, | ||
unbind() { | ||
window.removeEventListener('scroll', this.customData.onScroll); | ||
window.removeEventListener('scroll', Utils.debounce(this.customData.onScroll.bind(this))); | ||
}, | ||
}; |
@@ -6,3 +6,3 @@ import { Component, Binder } from '@ribajs/core'; | ||
public static tagName: string = 'bs4-dropdown'; | ||
public static tagName = 'bs4-dropdown'; | ||
@@ -13,3 +13,3 @@ protected scope: any = { | ||
protected dropdownService: DropdownService; | ||
protected dropdownService?: DropdownService; | ||
@@ -22,4 +22,2 @@ static get observedAttributes() { | ||
super(element); | ||
this.dropdownService = new DropdownService(this.el.querySelector('.dropdown-toggle') as HTMLButtonElement | HTMLAnchorElement); | ||
this.init(Bs4DropdownComponent.observedAttributes); | ||
} | ||
@@ -30,5 +28,14 @@ | ||
event.stopPropagation(); | ||
if (!this.dropdownService) { | ||
throw new Error('DropdownService not ready!'); | ||
} | ||
return this.dropdownService.toggle(); | ||
} | ||
protected connectedCallback() { | ||
super.connectedCallback(); | ||
this.dropdownService = new DropdownService(this.el.querySelector('.dropdown-toggle') as HTMLButtonElement | HTMLAnchorElement); | ||
this.init(Bs4DropdownComponent.observedAttributes); | ||
} | ||
protected template() { | ||
@@ -35,0 +42,0 @@ return null; |
@@ -45,3 +45,3 @@ import { Component } from '@ribajs/core'; | ||
public static tagName: string = 'bs4-contents'; | ||
public static tagName = 'bs4-contents'; | ||
@@ -67,2 +67,6 @@ protected autobind = true; | ||
super(element); | ||
} | ||
protected connectedCallback() { | ||
super.connectedCallback(); | ||
this.init(Bs4ContentsComponent.observedAttributes); | ||
@@ -103,2 +107,7 @@ } | ||
protected async beforeBind() { | ||
await super.beforeBind(); | ||
} | ||
protected async afterBind() { | ||
await super.afterBind(); | ||
if (this.scope.headerParentSelector && this.scope.headersStart && this.scope.headersDepth) { | ||
@@ -115,6 +124,2 @@ this.wrapperElement = document.querySelector(this.scope.headerParentSelector) || undefined; | ||
protected async afterBind() { | ||
super.afterBind(); | ||
} | ||
protected requiredAttributes() { | ||
@@ -131,2 +136,3 @@ return ['headersStart', 'headersDepth', 'headerParentSelector']; | ||
super.disconnectedCallback(); | ||
this.scope.anchors = []; | ||
} | ||
@@ -133,0 +139,0 @@ |
@@ -5,3 +5,3 @@ import { Component } from '@ribajs/core'; | ||
public static tagName: string = 'bs4-icon'; | ||
public static tagName = 'bs4-icon'; | ||
@@ -18,11 +18,2 @@ static get observedAttributes() { | ||
super(element); | ||
this.el.setAttribute('aria-hidden', 'true'); | ||
this.el.setAttribute('role', 'img'); | ||
this.el.classList.add('iconset'); | ||
// set default values | ||
// this.attributeChangedCallback('size', null, 32, null); | ||
this.attributeChangedCallback('direction', null, 'top', null); | ||
this.init(Bs4IconComponent.observedAttributes); | ||
} | ||
@@ -35,9 +26,20 @@ | ||
if (name === 'src') { | ||
if (!newValue) { | ||
console.warn('The src attribute must have a value!'); | ||
return ''; | ||
} | ||
if (fetch) { | ||
fetch(newValue) | ||
.then((response) => { | ||
// console.debug('response.headers.get("content-type")', response.headers.get('content-type')); | ||
if (response.status !== 200) { | ||
throw new Error(response.statusText); | ||
console.error(response.statusText); | ||
return ''; | ||
} | ||
return response.text(); | ||
if (response.headers.get('content-type')?.indexOf('image/svg+xml') !== -1) { | ||
return response.text(); | ||
} else { | ||
console.error('[bs4-icon] Only svg\'s are supported! But content-type is ' + response.headers.get('content-type')); | ||
} | ||
return ''; | ||
}) | ||
@@ -58,3 +60,3 @@ .then((response) => { | ||
if (newValue.length > 0) { | ||
this.el.className.replace(/(^|\s)color-\S+/g, ''); | ||
this.el.className = this.el.className.replace(/(^|\s)color-\S+/g, ''); | ||
for (let i = 0; i < newValue.length; i++) { | ||
@@ -70,3 +72,3 @@ const newColor: string = newValue[i]; | ||
this.el.style.color = newValue; | ||
this.el.className.replace(/(^|\s)color-\S+/g, ''); | ||
this.el.className = this.el.className.replace(/(^|\s)color-\S+/g, ''); | ||
this.el.classList.add(`color-${newValue}`); | ||
@@ -80,3 +82,3 @@ } | ||
this.el.style.width = size + 'px'; | ||
this.el.className.replace(/(^|\s)size-\S+/g, ''); | ||
this.el.className = this.el.className.replace(/(^|\s)size-\S+/g, ''); | ||
this.el.classList.add(`size-${size}`); | ||
@@ -88,3 +90,3 @@ } | ||
this.el.style.width = width + 'px'; | ||
this.el.className.replace(/(^|\s)width-\S+/g, ''); | ||
this.el.className = this.el.className.replace(/(^|\s)width-\S+/g, ''); | ||
this.el.classList.add(`width-${width}`); | ||
@@ -96,3 +98,3 @@ } | ||
this.el.style.height = height + 'px'; | ||
this.el.className.replace(/(^|\s)height-\S+/g, ''); | ||
this.el.className = this.el.className.replace(/(^|\s)height-\S+/g, ''); | ||
this.el.classList.add(`height-${height}`); | ||
@@ -122,4 +124,4 @@ } | ||
this.el.className.replace(/(^|\s)direction-\S+/g, ''); | ||
this.el.className.replace(/(^|\s)rotate-\S+/g, ''); | ||
this.el.className = this.el.className.replace(/(^|\s)direction-\S+/g, ''); | ||
this.el.className = this.el.className.replace(/(^|\s)rotate-\S+/g, ''); | ||
this.el.className += ' ' + classString; | ||
@@ -129,2 +131,15 @@ } | ||
protected connectedCallback() { | ||
super.connectedCallback(); | ||
this.el.setAttribute('aria-hidden', 'true'); | ||
this.el.setAttribute('role', 'img'); | ||
this.el.classList.add('iconset'); | ||
this.init(Bs4IconComponent.observedAttributes); | ||
// set default values | ||
if (!this.scope.direction) { | ||
this.scope.direction = 'up'; | ||
this.attributeChangedCallback('direction', null, this.scope.direction, null); | ||
} | ||
} | ||
protected template() { | ||
@@ -131,0 +146,0 @@ return null; |
@@ -6,3 +6,3 @@ import { Component, EventDispatcher, Binder } from '@ribajs/core'; | ||
public static tagName: string = 'bs4-navbar'; | ||
public static tagName = 'bs4-navbar'; | ||
@@ -15,5 +15,6 @@ protected scope: any = { | ||
collapseSelector: '.navbar-collapse', | ||
animated: true, | ||
}; | ||
protected collapse?: NodeListOf<Element>; | ||
protected collapse?: NodeListOf<HTMLElement>; | ||
protected collapseService?: CollapseService; | ||
@@ -23,3 +24,3 @@ protected router?: EventDispatcher; | ||
static get observedAttributes() { | ||
return ['collapse-selector']; | ||
return ['collapse-selector', 'animated']; | ||
} | ||
@@ -33,3 +34,3 @@ | ||
if (this.collapseService) { | ||
this.collapseService.toggle(); | ||
this.collapseService.toggle(this.scope.animated); | ||
} | ||
@@ -44,3 +45,3 @@ if (event) { | ||
if (this.collapseService) { | ||
this.collapseService.show(); | ||
this.collapseService.show(this.scope.animated); | ||
} | ||
@@ -55,3 +56,3 @@ if (event) { | ||
if (this.collapseService) { | ||
this.collapseService.hide(); | ||
this.collapseService.hide(this.scope.animated); | ||
} | ||
@@ -133,3 +134,3 @@ if (event) { | ||
if (this.collapseService) { | ||
this.collapseService.hide(); | ||
this.collapseService.hide(this.scope.animated); | ||
} | ||
@@ -136,0 +137,0 @@ } |
@@ -25,3 +25,3 @@ import { Bs4ContentsComponent, Scope as Bs4ContentsComponentScope } from '../bs4-contents/bs4-contents.component'; | ||
public static tagName: string = 'bs4-scrollspy'; | ||
public static tagName = 'bs4-scrollspy'; | ||
@@ -49,2 +49,6 @@ protected autobind = true; | ||
super(element); | ||
} | ||
protected connectedCallback() { | ||
super.connectedCallback(); | ||
this.init(Bs4ScrollspyComponent.observedAttributes); | ||
@@ -51,0 +55,0 @@ } |
@@ -23,5 +23,5 @@ import { | ||
/** | ||
* The width of the sidebar | ||
* The width of the sidebar with unit | ||
*/ | ||
width: number; | ||
width: string; | ||
@@ -42,2 +42,6 @@ // Options | ||
/** | ||
* Watch the routers `newPageReady` event to update the sidebar state, e.g. hide on slime than after route changes | ||
*/ | ||
watchNewPageReadyEvent: boolean; | ||
/** | ||
* You can force to hide the sidebar on corresponding URL pathames e.g. you can hide the sidebar on home with `['/']`. | ||
@@ -72,5 +76,5 @@ */ | ||
public static tagName: string = 'bs4-sidebar'; | ||
public static tagName = 'bs4-sidebar'; | ||
protected style: CSSStyleDeclaration; | ||
protected style?: CSSStyleDeclaration; | ||
@@ -80,3 +84,14 @@ protected autobind = true; | ||
static get observedAttributes() { | ||
return ['id', 'container-selector', 'position', 'width', 'auto-show-in-wider-than', 'auto-hide-on-slimmer-than', 'force-hide-on-location-pathnames', 'force-show-on-location-pathnames', 'overlay-on-slimmer-than']; | ||
return [ | ||
'id', | ||
'container-selector', | ||
'position', | ||
'width', | ||
'auto-show-on-wider-than', | ||
'auto-hide-on-slimmer-than', | ||
'force-hide-on-location-pathnames', | ||
'force-show-on-location-pathnames', | ||
'overlay-on-slimmer-than', | ||
'watch-new-page-ready-event', | ||
]; | ||
} | ||
@@ -94,3 +109,3 @@ | ||
id: undefined, | ||
width: 250, | ||
width: '250px', | ||
@@ -101,2 +116,3 @@ // Options | ||
autoHideOnSlimmerThan: 1200, | ||
watchNewPageReadyEvent: true, | ||
forceHideOnLocationPathnames: [], | ||
@@ -114,5 +130,2 @@ forceShowOnLocationPathnames: [], | ||
super(element); | ||
this.init(Bs4SidebarComponent.observedAttributes); | ||
this.style = window.getComputedStyle(this.el); | ||
window.addEventListener('resize', this.onEnviromentChanges.bind(this), false); | ||
} | ||
@@ -151,3 +164,12 @@ | ||
protected onToggle(targetId: string) { | ||
protected connectedCallback() { | ||
super.connectedCallback(); | ||
this.init(Bs4SidebarComponent.observedAttributes); | ||
this.style = window.getComputedStyle(this.el); | ||
window.addEventListener('resize', this.onEnviromentChanges.bind(this), false); | ||
// inital | ||
this.onEnviromentChanges(); | ||
} | ||
protected onToggle() { | ||
this.toggle(); | ||
@@ -158,10 +180,14 @@ } | ||
if (this.toggleButtonEvents) { | ||
this.toggleButtonEvents.off('toggle', this.onToggle); | ||
this.toggleButtonEvents.off('toggle', this.onToggle.bind(this)); | ||
this.toggleButtonEvents.off('init', this.triggerState.bind(this)); | ||
} | ||
this.toggleButtonEvents = new EventDispatcher('bs4-toggle-button:' + this.scope.id); | ||
this.toggleButtonEvents.on('toggle', this.onToggle.bind(this)); | ||
this.toggleButtonEvents.on('init', this.triggerState.bind(this)); | ||
} | ||
protected initRouterEventDispatcher() { | ||
this.routerEvents.on('newPageReady', this.onEnviromentChanges.bind(this)); | ||
if (this.scope.watchNewPageReadyEvent) { | ||
this.routerEvents.on('newPageReady', this.onEnviromentChanges.bind(this)); | ||
} | ||
} | ||
@@ -172,3 +198,3 @@ | ||
const translateX = this.scope.position === 'left' ? '-100%' : '100%'; | ||
this.el.setAttribute('style', `transform:translateX(${translateX});width:${this.scope.width}px;`); | ||
this.el.setAttribute('style', `transform:translateX(${translateX});width:${this.scope.width};`); | ||
} | ||
@@ -178,3 +204,3 @@ | ||
this.setContainersStyle(undefined, '', directon); | ||
this.el.setAttribute('style', `transform:translateX(0);width:${this.scope.width}px;`); | ||
this.el.setAttribute('style', `transform:translateX(0);width:${this.scope.width};`); | ||
} | ||
@@ -184,5 +210,9 @@ | ||
this.setContainersStyle(undefined, '', directon); | ||
this.el.setAttribute('style', `transform:translateX(0);width:${this.scope.width}px;`); | ||
this.el.setAttribute('style', `transform:translateX(0);width:${this.scope.width};`); | ||
} | ||
protected triggerState() { | ||
this.toggleButtonEvents?.trigger('state', this.scope.state); | ||
} | ||
protected onStateChange() { | ||
@@ -208,3 +238,3 @@ switch (this.scope.state) { | ||
protected get width() { | ||
return this.el.offsetWidth || this.scope.width; | ||
return this.el.offsetWidth ? this.el.offsetWidth + 'px' : this.scope.width; | ||
} | ||
@@ -220,9 +250,6 @@ | ||
const vw = Utils.getViewportDimensions().w; | ||
if (vw < this.scope.autoHideOnSlimmerThan) { | ||
if (this.scope.autoHideOnSlimmerThan > -1 && vw < this.scope.autoHideOnSlimmerThan) { | ||
return this.hide(); | ||
} | ||
if (vw < this.scope.autoHideOnSlimmerThan) { | ||
return this.hide(); | ||
} | ||
if (vw > this.scope.autoShowOnWiderThan) { | ||
if (this.scope.autoShowOnWiderThan > -1 && vw > this.scope.autoShowOnWiderThan) { | ||
return this.show(); | ||
@@ -266,3 +293,3 @@ } | ||
*/ | ||
protected setContainerStyle(container: HTMLUnknownElement, style: string = '', move?: State) { | ||
protected setContainerStyle(container: HTMLUnknownElement, style = '', move?: State) { | ||
if (move) { | ||
@@ -275,6 +302,6 @@ const width = this.width; | ||
case 'fixed': | ||
style += 'left:' + width + 'px'; | ||
style += 'left:' + width; | ||
break; | ||
default: | ||
style += 'margin-left:' + width + 'px'; | ||
style += 'margin-left:' + width; | ||
break; | ||
@@ -286,6 +313,6 @@ } | ||
case 'fixed': | ||
style += 'right:' + width + 'px'; | ||
style += 'right:' + width; | ||
break; | ||
default: | ||
style += 'margin-right:' + width + 'px'; | ||
style += 'margin-right:' + width; | ||
break; | ||
@@ -298,12 +325,14 @@ } | ||
} | ||
return container.setAttribute('style', `transition:${this.style.transition};${style}`); | ||
return container.setAttribute('style', `transition:${this.style ? this.style.transition : ''};${style}`); | ||
} | ||
protected async beforeBind() { | ||
await super.beforeBind(); | ||
this.initRouterEventDispatcher(); | ||
this.onEnviromentChanges(); | ||
return this.onEnviromentChanges(); | ||
} | ||
protected async afterBind() { | ||
this.onEnviromentChanges(); | ||
await super.afterBind(); | ||
return this.onEnviromentChanges(); | ||
} | ||
@@ -328,2 +357,7 @@ | ||
super.disconnectedCallback(); | ||
this.toggleButtonEvents?.off('init', this.triggerState.bind(this)); | ||
this.toggleButtonEvents?.off('toggle', this.onToggle.bind(this)); | ||
this.toggleButtonEvents?.off('init', this.triggerState.bind(this)); | ||
this.routerEvents.off('newPageReady', this.onEnviromentChanges.bind(this)); | ||
window.removeEventListener('resize', this.onEnviromentChanges.bind(this), false); | ||
} | ||
@@ -330,0 +364,0 @@ |
@@ -1,4 +0,7 @@ | ||
import { Component, Binding, handleizeFormatter } from '@ribajs/core'; | ||
import template from './bs4-tabs.component.html'; | ||
import { handleizeFormatter } from '@ribajs/core'; | ||
import templateHorizontal from './bs4-tabs-horizontal.component.html'; | ||
import templateVertical from './bs4-tabs-vertical.component.html'; | ||
import { TemplatesComponent } from '../templates/templates.component'; | ||
export interface Tab { | ||
@@ -10,18 +13,48 @@ title: string; | ||
type?: string; | ||
index: number; | ||
} | ||
export interface Scope { | ||
tabs: Tab[]; | ||
items: Tab[]; | ||
activate: Bs4TabsComponent['activate']; | ||
deactivate: Bs4TabsComponent['activate']; | ||
deactivateAll: Bs4TabsComponent['deactivateAll']; | ||
optionTabsAutoHeight: boolean; | ||
optionTabsAngle: 'vertical' | 'horizontal'; | ||
} | ||
export class Bs4TabsComponent extends Component { | ||
export class Bs4TabsComponent extends TemplatesComponent { | ||
public static tagName: string = 'bs4-tabs'; | ||
public static tagName = 'bs4-tabs'; | ||
protected templateAttributes = [ | ||
{ | ||
name: 'title', | ||
required: true, | ||
}, | ||
{ | ||
name: 'handle', | ||
required: false, | ||
}, | ||
{ | ||
name: 'type', | ||
required: false, | ||
}, | ||
{ | ||
name: 'active', | ||
required: false, | ||
}, | ||
{ | ||
name: 'index', | ||
required: false, | ||
}, | ||
]; | ||
protected scope: Scope = { | ||
tabs: new Array<Tab>(), | ||
items: new Array<Tab>(), | ||
activate: this.activate, | ||
deactivate: this.deactivate, | ||
deactivateAll:this.deactivateAll, | ||
optionTabsAutoHeight: false, | ||
optionTabsAngle: 'horizontal', | ||
}; | ||
@@ -36,2 +69,3 @@ | ||
'option-tabs-auto-height', | ||
'option-tabs-angle', | ||
'tab-0-title', 'tab-0-content', 'tab-0-handle', | ||
@@ -62,7 +96,2 @@ 'tab-1-title', 'tab-1-content', 'tab-1-handle', | ||
super(element); | ||
this.addTabsByTemplate(); | ||
this.initTabs(); | ||
this.activateFirstTab(); | ||
this.init(Bs4TabsComponent.observedAttributes); | ||
} | ||
@@ -108,10 +137,26 @@ | ||
public deactivateAll() { | ||
for (const tab of this.scope.tabs) { | ||
tab.active = false; | ||
for (let index = 0; index < this.scope.items.length; index++) { | ||
const tab = this.scope.items[index]; | ||
this.deactivate(tab); | ||
} | ||
} | ||
public activate(tab: Tab, binding?: Binding, event?: Event) { | ||
public deactivate(tab: Tab) { | ||
tab.active = false; | ||
const firstTabContentChild = this.getTabContentChildByIndex(tab.index); | ||
if (firstTabContentChild) { | ||
this.triggerVisibilityChangedForElement(firstTabContentChild, tab.active); | ||
} | ||
} | ||
public activate(tab: Tab) { | ||
this.deactivateAll(); | ||
tab.active = true; | ||
const firstTabContentChild = this.getTabContentChildByIndex(tab.index); | ||
if (firstTabContentChild) { | ||
this.triggerVisibilityChangedForElement(firstTabContentChild as Element, tab.active); | ||
} | ||
if (event) { | ||
@@ -122,8 +167,32 @@ event.preventDefault(); | ||
public activateFirstTab() { | ||
if (this.scope.tabs.length > 0) { | ||
this.activate(this.scope.tabs[0]); | ||
protected activateFirstTab() { | ||
if (this.scope.items.length > 0) { | ||
this.activate(this.scope.items[0]); | ||
} | ||
} | ||
protected getTabContentChildByIndex(index: number) { | ||
return this.el.querySelector(`.tab-content .tab-pane:nth-child(${index + 1}) > *`) || undefined; | ||
} | ||
/** | ||
* Trigger `visibility-changed` for components that need to update if visibility changes. | ||
* E.g. this event is used the bs4-slideshow component | ||
* @param element | ||
* @param visibile | ||
*/ | ||
protected triggerVisibilityChangedForElement(element: Element, visibile: boolean) { | ||
setTimeout(() => { | ||
// Use this event to update any custom element when it becomes visibile | ||
element.dispatchEvent(new CustomEvent('visibility-changed', {detail: {visibile}})); | ||
}, 200); | ||
} | ||
protected connectedCallback() { | ||
super.connectedCallback(); | ||
this.initTabs(); | ||
this.activateFirstTab(); | ||
this.init(Bs4TabsComponent.observedAttributes); | ||
} | ||
protected setElements() { | ||
@@ -136,6 +205,5 @@ this.tabs = this.el.querySelectorAll('[role="tab"]'); | ||
protected resizeTabsArray(newSize: number) { | ||
while (newSize > this.scope.tabs.length) { | ||
this.scope.tabs.push({handle: '', title: '', content: '', active: false}); | ||
while (newSize > this.scope.items.length) { | ||
this.scope.items.push({handle: '', title: '', content: '', active: false, index: this.scope.items.length - 1}); | ||
} | ||
this.scope.tabs.length = newSize; | ||
} | ||
@@ -157,3 +225,3 @@ | ||
protected onResizeEventHandler(event: Event) { | ||
protected onResizeEventHandler() { | ||
this.setHeight(); | ||
@@ -182,14 +250,15 @@ } | ||
const index = Number(attributeName.replace(/[^0-9]/g, '')); | ||
if (index >= this.scope.tabs.length) { | ||
if (index >= this.scope.items.length) { | ||
this.resizeTabsArray(index + 1); | ||
} | ||
this.scope.items[index].index = index; | ||
if (attributeName.endsWith('Content')) { | ||
this.scope.tabs[index].content = newValue; | ||
this.scope.items[index].content = newValue; | ||
} | ||
if (attributeName.endsWith('Title')) { | ||
this.scope.tabs[index].title = newValue; | ||
this.scope.tabs[index].handle = this.scope.tabs[index].handle || handleizeFormatter.read(this.scope.tabs[index].title); | ||
this.scope.items[index].title = newValue; | ||
this.scope.items[index].handle = this.scope.items[index].handle || handleizeFormatter.read(this.scope.items[index].title); | ||
} | ||
if (attributeName.endsWith('Handle')) { | ||
this.scope.tabs[index].handle = newValue; | ||
this.scope.items[index].handle = newValue; | ||
} | ||
@@ -199,7 +268,7 @@ | ||
if ( | ||
this.scope.tabs.length > 0 && | ||
this.scope.tabs[0] && | ||
this.scope.tabs[0].content.length > 0 && | ||
this.scope.tabs[0].title.length > 0 && | ||
this.scope.tabs[0].handle.length > 0 | ||
this.scope.items.length > 0 && | ||
this.scope.items[0] && | ||
this.scope.items[0].content.length > 0 && | ||
this.scope.items[0].title.length > 0 && | ||
this.scope.items[0].handle.length > 0 | ||
) { | ||
@@ -210,25 +279,11 @@ this.activateFirstTab(); | ||
protected addTabByTemplate(tpl: HTMLTemplateElement) { | ||
const title = tpl.getAttribute('title'); | ||
if (!title) { | ||
console.error(new Error('template "title" attribute is required"')); | ||
return; | ||
protected transformTemplateAttributes(attributes: any, index: number) { | ||
attributes = super.transformTemplateAttributes(attributes, index); | ||
if (!attributes.handle && attributes.title) { | ||
attributes.handle = handleizeFormatter.read(attributes.title); | ||
} | ||
const handle = tpl.getAttribute('handle') || handleizeFormatter.read(title); | ||
if (!handle) { | ||
console.error(new Error('template "handle" attribute is required"')); | ||
return; | ||
} | ||
const type = tpl.getAttribute('type') || undefined; | ||
const content = tpl.innerHTML; | ||
this.scope.tabs.push({title, handle, content, active: false, type }); | ||
attributes.active = attributes.active || false; | ||
return attributes; | ||
} | ||
protected addTabsByTemplate() { | ||
const templates = this.el.querySelectorAll<HTMLTemplateElement>('template'); | ||
templates.forEach((tpl) => { | ||
this.addTabByTemplate(tpl); | ||
}); | ||
} | ||
protected parsedAttributeChangedCallback(attributeName: string, oldValue: any, newValue: any, namespace: string | null) { | ||
@@ -251,14 +306,10 @@ super.parsedAttributeChangedCallback(attributeName, oldValue, newValue, namespace); | ||
protected onlyTemplateChilds() { | ||
let allAreTemplates: boolean = true; | ||
this.el.childNodes.forEach((child) => { | ||
allAreTemplates = allAreTemplates && (child.nodeName === 'TEMPLATE' || child.nodeName === '#text'); | ||
}); | ||
return allAreTemplates; | ||
} | ||
protected template() { | ||
// Only set the component template if there no childs or the childs are templates | ||
if (!this.el.hasChildNodes() || this.onlyTemplateChilds()) { | ||
return template; | ||
if (!this.el.hasChildNodes() || this.hasOnlyTemplateChilds()) { | ||
if (this.scope.optionTabsAngle === 'horizontal') { | ||
return templateHorizontal; | ||
} else { | ||
return templateVertical; | ||
} | ||
} else { | ||
@@ -265,0 +316,0 @@ return null; |
@@ -15,8 +15,5 @@ import { | ||
// TODO extend from Bs4ButtonComponent | ||
export class Bs4ToggleButtonComponent extends Component { | ||
public static tagName: string = 'bs4-toggle-button'; | ||
protected autobind = true; | ||
static get observedAttributes() { | ||
@@ -26,2 +23,6 @@ return ['target-id']; | ||
public static tagName = 'bs4-toggle-button'; | ||
protected autobind = true; | ||
protected eventDispatcher?: EventDispatcher; | ||
@@ -38,3 +39,2 @@ | ||
super(element); | ||
this.init(Bs4ToggleButtonComponent.observedAttributes); | ||
} | ||
@@ -48,2 +48,13 @@ | ||
protected async afterBind() { | ||
await super.afterBind(); | ||
// Trigger init to trigger there current state of all the components that are connected to this component | ||
return this.eventDispatcher?.trigger('init', this.scope.targetId); | ||
} | ||
protected connectedCallback() { | ||
super.connectedCallback(); | ||
this.init(Bs4ToggleButtonComponent.observedAttributes); | ||
} | ||
protected onToggledEvent(state: State) { | ||
@@ -60,2 +71,4 @@ this.scope.state = state; | ||
this.eventDispatcher.on('toggled', this.onToggledEvent.bind(this)); | ||
// Triggered state triggered by `..trigger('init', ...` | ||
this.eventDispatcher.on('state', this.onToggledEvent.bind(this)); | ||
} | ||
@@ -62,0 +75,0 @@ |
@@ -6,4 +6,7 @@ export { Bs4DropdownComponent } from './bd4-dropdown/bs4-dropdown.component'; | ||
export { Bs4SidebarComponent } from './bs4-sidebar/bs4-sidebar.component'; | ||
export { Bs4SlideshowComponent } from './bs4-slideshow/bs4-slideshow.component'; | ||
export { Bs4ToggleButtonComponent } from './bs4-toggle-button/bs4-toggle-button.component'; | ||
export { Bs4NavbarComponent } from './bs4-navbar/bs4-navbar.component'; | ||
export { Bs4TabsComponent } from './bs4-tabs/bs4-tabs.component'; | ||
export { Bs4ButtonComponent } from './bs4-button/bs4-button.component'; | ||
export { Bs4AccordionComponent } from './bs4-accordion/bs4-accordion.component'; |
@@ -0,1 +1,2 @@ | ||
/** | ||
@@ -21,26 +22,109 @@ * | ||
SHOW : 'show', | ||
COLLAPSE : 'collapse', | ||
COLLAPSING : 'collapsing', | ||
COLLAPSED : 'collapsed', | ||
COLLAPSE : 'collapse', // hidden | ||
COLLAPSING : 'collapsing', // animation | ||
COLLAPSED : 'collapsed', // Button / trigger element class if collapse element is collapsed | ||
}; | ||
private targets: NodeListOf<Element> | Array<Element>; | ||
public static show(element: HTMLElement, animated = true) { | ||
element.dispatchEvent(new Event(CollapseService.EVENT.SHOW)); | ||
if (animated) { | ||
element.addEventListener('webkitTransitionEnd' as 'animationend', this.onShowTransitionEnd.bind(this, element), { once: true }); | ||
element.addEventListener('transitionend', this.onShowTransitionEnd.bind(this, element), { once: true }); | ||
element.classList.add(CollapseService.CLASSNAME.COLLAPSING); | ||
element.classList.remove(CollapseService.CLASSNAME.COLLAPSE); | ||
// Get and set height to start transition | ||
setTimeout(() => { | ||
// const duration = Utils.getTransitionDurationFromElement(element); | ||
if (element.firstElementChild) { | ||
const dimension = element.firstElementChild.getBoundingClientRect(); | ||
element.style.height = dimension.height + 'px'; | ||
} else { | ||
element.style.height = 'auto'; | ||
} | ||
}); | ||
} else { | ||
this.onShowTransitionEnd(element); | ||
} | ||
} | ||
constructor(targets: NodeListOf<Element> | Array<Element>) { | ||
public static hide(element: HTMLElement, animated = true) { | ||
element.dispatchEvent(new Event(CollapseService.EVENT.HIDE)); | ||
if (animated) { | ||
element.addEventListener('webkitTransitionEnd' as 'animationend', this.onHideTransitionEnd.bind(this, element), { once: true }); | ||
element.addEventListener('transitionend', this.onHideTransitionEnd.bind(this, element), { once: true }); | ||
element.style.height = element.getBoundingClientRect().height + 'px' ; | ||
setTimeout(() => { | ||
element.classList.add(CollapseService.CLASSNAME.COLLAPSING); | ||
element.classList.remove(CollapseService.CLASSNAME.COLLAPSE); | ||
element.classList.remove(CollapseService.CLASSNAME.SHOW); | ||
element.style.height = '0' ; | ||
}); | ||
} else { | ||
this.onHideTransitionEnd(element); | ||
} | ||
} | ||
public static hideAll(elements: NodeListOf<HTMLElement> | Array<HTMLElement>) { | ||
elements.forEach((element: HTMLElement) => { | ||
this.hide(element); | ||
}); | ||
} | ||
public static isExpanded(element: HTMLElement) { | ||
return element.classList.contains(CollapseService.CLASSNAME.SHOW); | ||
} | ||
public static isCollapsed(element: HTMLElement) { | ||
return !this.isExpanded(element); | ||
} | ||
public static toggle(element: HTMLElement, animated = true) { | ||
if (this.isCollapsed(element)) { | ||
this.show(element, animated); | ||
} else { | ||
this.hide(element, animated); | ||
} | ||
} | ||
protected static onShowTransitionEnd(element: HTMLElement) { | ||
setTimeout(() => { | ||
element.classList.add(CollapseService.CLASSNAME.COLLAPSE); | ||
element.classList.add(CollapseService.CLASSNAME.SHOW); | ||
element.classList.remove(CollapseService.CLASSNAME.COLLAPSING); | ||
element.style.height = ''; | ||
element.dispatchEvent(new Event(CollapseService.EVENT.SHOWN)); | ||
}); | ||
} | ||
protected static onHideTransitionEnd(element: HTMLElement) { | ||
setTimeout(() => { | ||
element.classList.add(CollapseService.CLASSNAME.COLLAPSE); | ||
element.classList.remove(CollapseService.CLASSNAME.COLLAPSING); | ||
element.classList.remove(CollapseService.CLASSNAME.SHOW); | ||
element.style.height = ''; | ||
element.dispatchEvent(new Event(CollapseService.EVENT.HIDDEN)); | ||
}); | ||
} | ||
protected targets: NodeListOf<HTMLElement> | Array<HTMLElement>; | ||
constructor(targets: NodeListOf<HTMLElement> | Array<HTMLElement>) { | ||
this.targets = targets; | ||
} | ||
public show() { | ||
this.targets.forEach((target: Element) => { | ||
target.classList.remove(CollapseService.CLASSNAME.COLLAPSE); | ||
target.classList.add(CollapseService.CLASSNAME.SHOW); | ||
target.dispatchEvent(new Event(CollapseService.EVENT.SHOWN)); | ||
/** | ||
* Show all | ||
*/ | ||
public show(animated = true) { | ||
this.targets.forEach((target: HTMLElement) => { | ||
CollapseService.show(target, animated); | ||
}); | ||
} | ||
public hide() { | ||
this.targets.forEach((target: Element) => { | ||
target.classList.remove(CollapseService.CLASSNAME.SHOW); | ||
target.classList.add(CollapseService.CLASSNAME.COLLAPSE); | ||
target.dispatchEvent(new Event(CollapseService.EVENT.HIDDEN)); | ||
/** | ||
* Collapse / hide all | ||
*/ | ||
public hide(animated = true) { | ||
this.targets.forEach((target: HTMLElement) => { | ||
CollapseService.hide(target, animated); | ||
}); | ||
@@ -50,6 +134,3 @@ } | ||
public isExpanded() { | ||
if (this.targets.length > 0 && this.targets[0]) { | ||
return this.targets[0].classList.contains(CollapseService.CLASSNAME.SHOW); | ||
} | ||
return false; | ||
return CollapseService.isExpanded(this.targets[0]); | ||
} | ||
@@ -61,9 +142,9 @@ | ||
public toggle() { | ||
public toggle(animated = true) { | ||
if (this.isCollapsed()) { | ||
this.show(); | ||
this.show(animated); | ||
} else { | ||
this.hide(); | ||
this.hide(animated); | ||
} | ||
} | ||
} |
@@ -111,5 +111,4 @@ import Popper from 'popper.js'; // /dist/umd/popper | ||
public static closeAll() { | ||
console.debug('closeAll'); | ||
const buttons = document.querySelectorAll(SELECTOR.DATA_TOGGLE); | ||
buttons.forEach((button, index) => { | ||
buttons.forEach((button) => { | ||
if (button.parentElement) { | ||
@@ -147,3 +146,3 @@ const menu = button.parentElement.querySelector(SELECTOR.MENU + '.' + CLASSNAME.SHOW) || undefined; | ||
public static _clearMenus(event?: MouseEvent) { | ||
public static _clearMenus() { | ||
return this.closeAll(); | ||
@@ -290,2 +289,10 @@ } | ||
private outsideClickListener(element: Element, event: Event) { | ||
const target = event.target || event.srcElement || event.currentTarget; | ||
if (target && !element.contains(target as Node)) { | ||
this.close(); | ||
document.removeEventListener('click', this.outsideClickListener.bind(this, element)); | ||
} | ||
} | ||
/** | ||
@@ -296,15 +303,3 @@ * @see https://stackoverflow.com/questions/152975/how-do-i-detect-a-click-outside-an-element | ||
private clouseOnClickOutsite(element: Element) { | ||
const outsideClickListener = (event: Event) => { | ||
const target = event.target || event.srcElement || event.currentTarget; | ||
if (target && !element.contains(target as Node)) { | ||
this.close(); | ||
removeClickListener(); | ||
} | ||
}; | ||
const removeClickListener = () => { | ||
document.removeEventListener('click', outsideClickListener); | ||
}; | ||
document.addEventListener('click', outsideClickListener); | ||
document.addEventListener('click', this.outsideClickListener.bind(this, element)); | ||
} | ||
@@ -311,0 +306,0 @@ |
@@ -5,2 +5,4 @@ import { | ||
const MILLISECONDS_MULTIPLIER = 1000; | ||
/** | ||
@@ -51,2 +53,24 @@ * | ||
} | ||
// https://github.com/twbs/bootstrap/blob/master/dist/js/bootstrap.bundle.js#L137 | ||
public static getTransitionDurationFromElement(element: HTMLElement) { | ||
if (!element) { | ||
return 0; | ||
} // Get transition-duration of the element | ||
const _window$getComputedSt = window.getComputedStyle(element); | ||
let transitionDuration = _window$getComputedSt.transitionDuration; | ||
let transitionDelay = _window$getComputedSt.transitionDelay; | ||
const floatTransitionDuration = parseFloat(transitionDuration); | ||
const floatTransitionDelay = parseFloat(transitionDelay); // Return 0 if element or transition duration is not found | ||
if (!floatTransitionDuration && !floatTransitionDelay) { | ||
return 0; | ||
} // If multiple durations are defined, take the first | ||
transitionDuration = transitionDuration.split(',')[0]; | ||
transitionDelay = transitionDelay.split(',')[0]; | ||
return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; | ||
} | ||
} |
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"baseUrl": "./src/", | ||
"outDir": "./lib/", | ||
"module": "commonjs", | ||
"target": "esnext", | ||
"lib": ["es7", "dom"], | ||
"moduleResolution": "node", | ||
"typeRoots": ["./node_modules/@types", "./node_modules/@ribajs/core/src/types"], | ||
"outDir": "dist", | ||
"declaration": true, | ||
"inlineSourceMap": true, | ||
"noUnusedLocals": true, | ||
"typeRoots": ["./node_modules/@types", "./src/types"], | ||
"types": ["jest"] | ||
"esModuleInterop": true, | ||
"strict": true, | ||
"noImplicitAny": true, | ||
"noImplicitThis": true, | ||
"allowSyntheticDefaultImports": true | ||
}, | ||
"include": ["*.ts", "**/*.ts"] | ||
"include": ["src"], | ||
"exclude": ["**/*.spec.ts"], | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
152067
71
3253
4
23
+ Added@ribajs/extras@^1.8.0
+ Added@ribajs/extras@1.9.0(transitive)
Updated@ribajs/core@^1.8.0
Updatedbootstrap@^4.4.1