mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-25 09:42:10 +08:00
TS: Converted app file and animations service
Extracted functions out of app file during changes to clean up. Altered animation function to use normal css prop names instead of JS CSS prop names.
This commit is contained in:
parent
2e8d6ce7d9
commit
f41c02cbd7
|
@ -1,33 +0,0 @@
|
|||
import {EventManager} from './services/events.ts';
|
||||
import {HttpManager} from './services/http.ts';
|
||||
import {Translator} from './services/translations.ts';
|
||||
import * as componentMap from './components';
|
||||
import {ComponentStore} from './services/components.ts';
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
window.__DEV__ = false;
|
||||
|
||||
// Url retrieval function
|
||||
window.baseUrl = function baseUrl(path) {
|
||||
let targetPath = path;
|
||||
let basePath = document.querySelector('meta[name="base-url"]').getAttribute('content');
|
||||
if (basePath[basePath.length - 1] === '/') basePath = basePath.slice(0, basePath.length - 1);
|
||||
if (targetPath[0] === '/') targetPath = targetPath.slice(1);
|
||||
return `${basePath}/${targetPath}`;
|
||||
};
|
||||
|
||||
window.importVersioned = function importVersioned(moduleName) {
|
||||
const version = document.querySelector('link[href*="/dist/styles.css?version="]').href.split('?version=').pop();
|
||||
const importPath = window.baseUrl(`dist/${moduleName}.js?version=${version}`);
|
||||
return import(importPath);
|
||||
};
|
||||
|
||||
// Set events, http & translation services on window
|
||||
window.$http = new HttpManager();
|
||||
window.$events = new EventManager();
|
||||
window.$trans = new Translator();
|
||||
|
||||
// Load & initialise components
|
||||
window.$components = new ComponentStore();
|
||||
window.$components.register(componentMap);
|
||||
window.$components.init();
|
23
resources/js/app.ts
Normal file
23
resources/js/app.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import {EventManager} from './services/events';
|
||||
import {HttpManager} from './services/http';
|
||||
import {Translator} from './services/translations';
|
||||
import * as componentMap from './components/index';
|
||||
import {ComponentStore} from './services/components';
|
||||
import {baseUrl, importVersioned} from "./services/util";
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
window.__DEV__ = false;
|
||||
|
||||
// Make common important util functions global
|
||||
window.baseUrl = baseUrl;
|
||||
window.importVersioned = importVersioned;
|
||||
|
||||
// Setup events, http & translation services
|
||||
window.$http = new HttpManager();
|
||||
window.$events = new EventManager();
|
||||
window.$trans = new Translator();
|
||||
|
||||
// Load & initialise components
|
||||
window.$components = new ComponentStore();
|
||||
window.$components.register(componentMap);
|
||||
window.$components.init();
|
|
@ -1,4 +1,4 @@
|
|||
import {slideUp, slideDown} from '../services/animations';
|
||||
import {slideUp, slideDown} from '../services/animations.ts';
|
||||
import {Component} from './component';
|
||||
|
||||
export class ChapterContents extends Component {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {slideDown, slideUp} from '../services/animations';
|
||||
import {slideDown, slideUp} from '../services/animations.ts';
|
||||
import {Component} from './component';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {debounce} from '../services/util.ts';
|
||||
import {transitionHeight} from '../services/animations';
|
||||
import {transitionHeight} from '../services/animations.ts';
|
||||
import {Component} from './component';
|
||||
|
||||
export class DropdownSearch extends Component {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {slideUp, slideDown} from '../services/animations';
|
||||
import {slideUp, slideDown} from '../services/animations.ts';
|
||||
import {Component} from './component';
|
||||
|
||||
export class ExpandToggle extends Component {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {fadeIn, fadeOut} from '../services/animations';
|
||||
import {fadeIn, fadeOut} from '../services/animations.ts';
|
||||
import {onSelect} from '../services/dom';
|
||||
import {Component} from './component';
|
||||
|
||||
|
|
2
resources/js/global.d.ts
vendored
2
resources/js/global.d.ts
vendored
|
@ -7,10 +7,12 @@ declare global {
|
|||
const __DEV__: boolean;
|
||||
|
||||
interface Window {
|
||||
__DEV__: boolean;
|
||||
$components: ComponentStore;
|
||||
$events: EventManager;
|
||||
$trans: Translator;
|
||||
$http: HttpManager;
|
||||
baseUrl: (path: string) => string;
|
||||
importVersioned: (module: string) => Promise<object>;
|
||||
}
|
||||
}
|
|
@ -1,30 +1,30 @@
|
|||
/**
|
||||
* Used in the function below to store references of clean-up functions.
|
||||
* Used to ensure only one transitionend function exists at any time.
|
||||
* @type {WeakMap<object, any>}
|
||||
*/
|
||||
const animateStylesCleanupMap = new WeakMap();
|
||||
const animateStylesCleanupMap: WeakMap<object, any> = new WeakMap();
|
||||
|
||||
/**
|
||||
* Animate the css styles of an element using FLIP animation techniques.
|
||||
* Styles must be an object where the keys are style properties, camelcase, and the values
|
||||
* Styles must be an object where the keys are style rule names and the values
|
||||
* are an array of two items in the format [initialValue, finalValue]
|
||||
* @param {Element} element
|
||||
* @param {Object} styles
|
||||
* @param {Number} animTime
|
||||
* @param {Function} onComplete
|
||||
*/
|
||||
function animateStyles(element, styles, animTime = 400, onComplete = null) {
|
||||
function animateStyles(
|
||||
element: HTMLElement,
|
||||
styles: Record<string, string[]>,
|
||||
animTime: number = 400,
|
||||
onComplete: Function | null = null
|
||||
): void {
|
||||
const styleNames = Object.keys(styles);
|
||||
for (const style of styleNames) {
|
||||
element.style[style] = styles[style][0];
|
||||
element.style.setProperty(style, styles[style][0]);
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
for (const style of styleNames) {
|
||||
element.style[style] = null;
|
||||
element.style.removeProperty(style);
|
||||
}
|
||||
element.style.transition = null;
|
||||
element.style.removeProperty('transition');
|
||||
element.removeEventListener('transitionend', cleanup);
|
||||
animateStylesCleanupMap.delete(element);
|
||||
if (onComplete) onComplete();
|
||||
|
@ -33,7 +33,7 @@ function animateStyles(element, styles, animTime = 400, onComplete = null) {
|
|||
setTimeout(() => {
|
||||
element.style.transition = `all ease-in-out ${animTime}ms`;
|
||||
for (const style of styleNames) {
|
||||
element.style[style] = styles[style][1];
|
||||
element.style.setProperty(style, styles[style][1]);
|
||||
}
|
||||
|
||||
element.addEventListener('transitionend', cleanup);
|
||||
|
@ -43,9 +43,8 @@ function animateStyles(element, styles, animTime = 400, onComplete = null) {
|
|||
|
||||
/**
|
||||
* Run the active cleanup action for the given element.
|
||||
* @param {Element} element
|
||||
*/
|
||||
function cleanupExistingElementAnimation(element) {
|
||||
function cleanupExistingElementAnimation(element: Element) {
|
||||
if (animateStylesCleanupMap.has(element)) {
|
||||
const oldCleanup = animateStylesCleanupMap.get(element);
|
||||
oldCleanup();
|
||||
|
@ -54,15 +53,12 @@ function cleanupExistingElementAnimation(element) {
|
|||
|
||||
/**
|
||||
* Fade in the given element.
|
||||
* @param {Element} element
|
||||
* @param {Number} animTime
|
||||
* @param {Function|null} onComplete
|
||||
*/
|
||||
export function fadeIn(element, animTime = 400, onComplete = null) {
|
||||
export function fadeIn(element: HTMLElement, animTime: number = 400, onComplete: Function | null = null): void {
|
||||
cleanupExistingElementAnimation(element);
|
||||
element.style.display = 'block';
|
||||
animateStyles(element, {
|
||||
opacity: ['0', '1'],
|
||||
'opacity': ['0', '1'],
|
||||
}, animTime, () => {
|
||||
if (onComplete) onComplete();
|
||||
});
|
||||
|
@ -70,14 +66,11 @@ export function fadeIn(element, animTime = 400, onComplete = null) {
|
|||
|
||||
/**
|
||||
* Fade out the given element.
|
||||
* @param {Element} element
|
||||
* @param {Number} animTime
|
||||
* @param {Function|null} onComplete
|
||||
*/
|
||||
export function fadeOut(element, animTime = 400, onComplete = null) {
|
||||
export function fadeOut(element: HTMLElement, animTime: number = 400, onComplete: Function | null = null): void {
|
||||
cleanupExistingElementAnimation(element);
|
||||
animateStyles(element, {
|
||||
opacity: ['1', '0'],
|
||||
'opacity': ['1', '0'],
|
||||
}, animTime, () => {
|
||||
element.style.display = 'none';
|
||||
if (onComplete) onComplete();
|
||||
|
@ -86,20 +79,18 @@ export function fadeOut(element, animTime = 400, onComplete = null) {
|
|||
|
||||
/**
|
||||
* Hide the element by sliding the contents upwards.
|
||||
* @param {Element} element
|
||||
* @param {Number} animTime
|
||||
*/
|
||||
export function slideUp(element, animTime = 400) {
|
||||
export function slideUp(element: HTMLElement, animTime: number = 400) {
|
||||
cleanupExistingElementAnimation(element);
|
||||
const currentHeight = element.getBoundingClientRect().height;
|
||||
const computedStyles = getComputedStyle(element);
|
||||
const currentPaddingTop = computedStyles.getPropertyValue('padding-top');
|
||||
const currentPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
|
||||
const animStyles = {
|
||||
maxHeight: [`${currentHeight}px`, '0px'],
|
||||
overflow: ['hidden', 'hidden'],
|
||||
paddingTop: [currentPaddingTop, '0px'],
|
||||
paddingBottom: [currentPaddingBottom, '0px'],
|
||||
'max-height': [`${currentHeight}px`, '0px'],
|
||||
'overflow': ['hidden', 'hidden'],
|
||||
'padding-top': [currentPaddingTop, '0px'],
|
||||
'padding-bottom': [currentPaddingBottom, '0px'],
|
||||
};
|
||||
|
||||
animateStyles(element, animStyles, animTime, () => {
|
||||
|
@ -109,10 +100,8 @@ export function slideUp(element, animTime = 400) {
|
|||
|
||||
/**
|
||||
* Show the given element by expanding the contents.
|
||||
* @param {Element} element - Element to animate
|
||||
* @param {Number} animTime - Animation time in ms
|
||||
*/
|
||||
export function slideDown(element, animTime = 400) {
|
||||
export function slideDown(element: HTMLElement, animTime: number = 400) {
|
||||
cleanupExistingElementAnimation(element);
|
||||
element.style.display = 'block';
|
||||
const targetHeight = element.getBoundingClientRect().height;
|
||||
|
@ -120,10 +109,10 @@ export function slideDown(element, animTime = 400) {
|
|||
const targetPaddingTop = computedStyles.getPropertyValue('padding-top');
|
||||
const targetPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
|
||||
const animStyles = {
|
||||
maxHeight: ['0px', `${targetHeight}px`],
|
||||
overflow: ['hidden', 'hidden'],
|
||||
paddingTop: ['0px', targetPaddingTop],
|
||||
paddingBottom: ['0px', targetPaddingBottom],
|
||||
'max-height': ['0px', `${targetHeight}px`],
|
||||
'overflow': ['hidden', 'hidden'],
|
||||
'padding-top': ['0px', targetPaddingTop],
|
||||
'padding-bottom': ['0px', targetPaddingBottom],
|
||||
};
|
||||
|
||||
animateStyles(element, animStyles, animTime);
|
||||
|
@ -134,11 +123,8 @@ export function slideDown(element, animTime = 400) {
|
|||
* Call with first state, and you'll receive a function in return.
|
||||
* Call the returned function in the second state to animate between those two states.
|
||||
* If animating to/from 0-height use the slide-up/slide down as easier alternatives.
|
||||
* @param {Element} element - Element to animate
|
||||
* @param {Number} animTime - Animation time in ms
|
||||
* @returns {function} - Function to run in second state to trigger animation.
|
||||
*/
|
||||
export function transitionHeight(element, animTime = 400) {
|
||||
export function transitionHeight(element: HTMLElement, animTime: number = 400): () => void {
|
||||
const startHeight = element.getBoundingClientRect().height;
|
||||
const initialComputedStyles = getComputedStyle(element);
|
||||
const startPaddingTop = initialComputedStyles.getPropertyValue('padding-top');
|
||||
|
@ -151,10 +137,10 @@ export function transitionHeight(element, animTime = 400) {
|
|||
const targetPaddingTop = computedStyles.getPropertyValue('padding-top');
|
||||
const targetPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
|
||||
const animStyles = {
|
||||
height: [`${startHeight}px`, `${targetHeight}px`],
|
||||
overflow: ['hidden', 'hidden'],
|
||||
paddingTop: [startPaddingTop, targetPaddingTop],
|
||||
paddingBottom: [startPaddingBottom, targetPaddingBottom],
|
||||
'height': [`${startHeight}px`, `${targetHeight}px`],
|
||||
'overflow': ['hidden', 'hidden'],
|
||||
'padding-top': [startPaddingTop, targetPaddingTop],
|
||||
'padding-bottom': [startPaddingBottom, targetPaddingBottom],
|
||||
};
|
||||
|
||||
animateStyles(element, animStyles, animTime);
|
|
@ -99,3 +99,49 @@ export function wait(timeMs: number): Promise<any> {
|
|||
setTimeout(res, timeMs);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a full URL from the given relative URL, using a base
|
||||
* URL defined in the head of the page.
|
||||
*/
|
||||
export function baseUrl(path: string): string {
|
||||
let targetPath = path;
|
||||
const baseUrlMeta = document.querySelector('meta[name="base-url"]');
|
||||
if (!baseUrlMeta) {
|
||||
throw new Error('Could not find expected base-url meta tag in document');
|
||||
}
|
||||
|
||||
let basePath = baseUrlMeta.getAttribute('content') || '';
|
||||
if (basePath[basePath.length - 1] === '/') {
|
||||
basePath = basePath.slice(0, basePath.length - 1);
|
||||
}
|
||||
|
||||
if (targetPath[0] === '/') {
|
||||
targetPath = targetPath.slice(1);
|
||||
}
|
||||
|
||||
return `${basePath}/${targetPath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current version of BookStack in use.
|
||||
* Grabs this from the version query used on app assets.
|
||||
*/
|
||||
function getVersion(): string {
|
||||
const styleLink = document.querySelector('link[href*="/dist/styles.css?version="]');
|
||||
if (!styleLink) {
|
||||
throw new Error('Could not find expected style link in document for version use');
|
||||
}
|
||||
|
||||
const href = (styleLink.getAttribute('href') || '');
|
||||
return href.split('?version=').pop() || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a module import, Ensuring the import is fetched with the current
|
||||
* app version as a cache-breaker.
|
||||
*/
|
||||
export function importVersioned(moduleName: string): Promise<object> {
|
||||
const importPath = window.baseUrl(`dist/${moduleName}.js?version=${getVersion()}`);
|
||||
return import(importPath);
|
||||
}
|
Loading…
Reference in New Issue
Block a user