Merge pull request #455 from KingSora/v2.0.0

v2.0.0
This commit is contained in:
Rene Haas
2022-08-11 11:34:57 +02:00
committed by GitHub
316 changed files with 38195 additions and 66032 deletions
+6
View File
@@ -0,0 +1,6 @@
node_modules
dist
types
.build
.coverage
.nyc_output
+125
View File
@@ -0,0 +1,125 @@
const resolve = require('@~local/config/resolve');
module.exports = {
extends: ['airbnb', 'prettier'],
env: {
browser: true,
es2020: true,
node: true,
jest: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 11,
sourceType: 'module',
},
settings: {
'import/resolver': {
node: {
extensions: resolve.extensions,
moduleDirectory: resolve.directories,
},
},
},
plugins: ['prettier', 'json', 'react', 'jest', 'import', '@typescript-eslint'],
rules: {
'func-names': 'off',
'no-plusplus': 'off',
'no-continue': 'off',
'no-param-reassign': 'off',
'no-nested-ternary': 'off',
'no-underscore-dangle': 'off',
'no-multi-assign': 'off',
'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
'no-console': ['error', { allow: ['warn', 'error'] }],
'no-empty': ['error', { allowEmptyCatch: true }],
'no-cond-assign': ['error', 'except-parens'],
camelcase: ['error', { allow: ['^__', '^UNSAFE_'] }],
'prefer-destructuring': 'off',
'consistent-return': 'off',
'import/prefer-default-export': 'off',
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': [
'error',
{
ignore: [`^@/.*`],
},
],
'import/extensions': [
'off',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
},
overrides: [
{
files: ['*.ts', '*.tsx', '*.d.ts'],
extends: ['plugin:@typescript-eslint/recommended', 'plugin:react/recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./packages/**/tsconfig.json', './local/**/tsconfig.json'],
},
rules: {
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': ['error'],
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': ['error', { ignoreDeclarationMerge: true }],
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/no-this-alias': [
'error',
{
allowDestructuring: true, // Allow `const { props, state } = this`; false by default
allowedNames: ['self', '_self'], // Allow `const self = this`; `[]` by default
},
],
},
},
{
files: ['*.test.*', '**/tests/**'],
rules: {
'no-shadow': 'off',
'no-use-before-define': 'off',
'no-restricted-syntax': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'no-console': 'off',
'consistent-return': 'off',
'symbol-description': 'off',
'no-new-wrappers': 'off',
'no-prototype-builtins': 'off',
'no-void': 'off',
'no-empty-function': 'off',
'no-new-func': 'off',
'import/order': 'off',
},
},
{
files: ['*rollup*'],
rules: {
'no-console': 'off',
'global-require': 'off',
'import/no-dynamic-require': 'off',
},
},
],
};
+1 -1
View File
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: ['https://www.paypal.me/ReneHaas'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+5 -4
View File
@@ -4,7 +4,6 @@ about: Create a report to help improve OverlayScrollbars
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@@ -21,12 +21,13 @@ Steps to reproduce the behavior:
A clear and concise description of what you expected to happen.
**Examples**
Please create a small example of the bug.
Please create a small example of the bug.
To do this you can use online platforms like [JSFiddle](https://jsfiddle.net/), [CodeSandbox](https://codesandbox.io/) or [StackBlitz](https://stackblitz.com/). You can also create a separate Github repository which I can clone.
**Environment**
- Used Operating System(s):
- Used Browser(s) (with version):
- Used Operating System(s):
- Used Browser(s) (with version):
**Additional context**
Add any other context about the problem here.
@@ -4,7 +4,6 @@ about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
+10 -3
View File
@@ -3,9 +3,16 @@
# dependencies
node_modules/
# dist
example/dist/
example/build/
# generated by tools
.build/
.coverage/
.nyc_output/
# generated by build
packages/**/dist/
packages/**/styles/
packages/**/types/
local/**/dist/
# local env files
.env.local
+1
View File
@@ -0,0 +1 @@
registry="https://registry.npmjs.org"
+6
View File
@@ -0,0 +1,6 @@
node_modules
dist
types
.coverage
.build
.nyc_output
+36
View File
@@ -0,0 +1,36 @@
{
"printWidth": 100,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "es5",
"jsxBracketSameLine": true,
"arrowParens": "always",
"endOfLine": "lf",
"parser": "babel-ts",
"overrides": [
{
"files": "*.html",
"options": { "parser": "html" }
},
{
"files": "*.css",
"options": { "parser": "css" }
},
{
"files": "*.scss",
"options": { "parser": "scss" }
},
{
"files": "*.vue",
"options": { "parser": "vue" }
},
{
"files": "*.component.html",
"options": { "parser": "angular" }
},
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}
+367 -682
View File
File diff suppressed because it is too large Load Diff
-75
View File
@@ -1,75 +0,0 @@
const fs = require('fs');
const sh = require('shelljs');
const chalk = require('chalk');
const gulp = require('gulp');
const uglify = require('uglify-js');
const csso = require('csso');
const uglifyFiles = [
{
src: './js/OverlayScrollbars.js',
dest: './js/OverlayScrollbars.min.js',
},
{
src: './js/jquery.overlayScrollbars.js',
dest: './js/jquery.overlayScrollbars.min.js',
}
];
const cssoFiles = [
{
src: './css/OverlayScrollbars.css',
dest: './css/OverlayScrollbars.min.css',
}
];
sh.echo(chalk.cyanBright('Start building:'));
gulp.task('uglify', function (done) {
uglifyFiles.forEach((file) => {
if (sh.test('-f', file.src)) {
sh.echo(chalk.yellowBright('uglify: ') + chalk.greenBright(`${file.src}${file.dest}`));
sh.ShellString(uglify.minify(fs.readFileSync(file.src, 'utf-8'), {
ie8: true,
compress: {
ie8: true
},
mangle: {
ie8: true,
properties: {
'regex': /^_/
}
},
output: {
ie8: true,
beautify: false,
comments: /@license|@preserve|^!/,
indent_level: 4,
indent_start: 0,
quote_style: 0
}
}).code).to(file.dest);
}
else {
sh.echo(chalk.redBright(`${file.src} not found!`));
}
});
done();
});
gulp.task('csso', function (done) {
cssoFiles.forEach((file) => {
if (sh.test('-f', file.src)) {
sh.echo(chalk.yellowBright('csso: ') + chalk.greenBright(`${file.src}${file.dest}`));
sh.ShellString(csso.minify(fs.readFileSync(file.src, 'utf-8'), {
restructure: false,
comments: 'first-exclamation'
}).css).to(file.dest);
}
else {
sh.echo(chalk.redBright(`${file.src} not found!`));
}
});
done();
});
gulp.parallel('uglify', 'csso')();
File diff suppressed because one or more lines are too long
-13
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
-13
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
{
"private": true,
"name": "browser-testing",
"version": "0.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"postinstall": "tsc --project ./tsconfig.json"
}
}
+108
View File
@@ -0,0 +1,108 @@
// @ts-ignore
import {
createDiv,
appendChildren,
parent,
style,
on,
off,
addClass,
WH,
XY,
clientSize,
each,
} from './support';
type ResizeListener = (width: number, height: number) => void;
export const resize = (element: HTMLElement) => {
const resizeListeners: ResizeListener[] = [];
const strMouseTouchDownEvent = 'mousedown touchstart';
const strMouseTouchUpEvent = 'mouseup touchend';
const strMouseTouchMoveEvent = 'mousemove touchmove';
const strSelectStartEvent = 'selectstart';
const dragStartSize: WH<number> = { w: 0, h: 0 };
const dragStartPosition: XY<number> = { x: 0, y: 0 };
const resizeBtn = createDiv('resizeBtn');
appendChildren(element, resizeBtn);
addClass(element, 'resizer');
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let dragResizeBtn: HTMLElement | undefined;
let dragResizer: HTMLElement | undefined;
const onSelectStart = (event: Event) => {
event.preventDefault();
return false;
};
const resizerResize = (event: MouseEvent | TouchEvent) => {
const isTouchEvent = (event as TouchEvent).touches !== undefined;
const mouseOffsetHolder = isTouchEvent
? (event as TouchEvent).touches[0]
: (event as MouseEvent);
const sizeStyle = {
width: dragStartSize.w + mouseOffsetHolder.pageX - dragStartPosition.x,
height: dragStartSize.h + mouseOffsetHolder.pageY - dragStartPosition.y,
};
style(dragResizer, sizeStyle);
each(resizeListeners, (listener: ResizeListener) => {
if (listener) {
listener(sizeStyle.width, sizeStyle.height);
}
});
event.stopPropagation();
};
const resizerResized = () => {
off(document, strSelectStartEvent, onSelectStart);
off(document, strMouseTouchMoveEvent, resizerResize);
off(document, strMouseTouchUpEvent, resizerResized);
dragResizer = undefined;
dragResizeBtn = undefined;
};
on(
resizeBtn,
strMouseTouchDownEvent,
(event: MouseEvent | TouchEvent) => {
const { currentTarget } = event;
const correctButton = (event as MouseEvent).buttons === 1 || event.which === 1;
const isTouchEvent = (event as TouchEvent).touches !== undefined;
const mouseOffsetHolder = isTouchEvent
? (event as TouchEvent).touches[0]
: (event as MouseEvent);
if (correctButton || isTouchEvent) {
dragStartPosition.x = mouseOffsetHolder.pageX;
dragStartPosition.y = mouseOffsetHolder.pageY;
dragResizeBtn = currentTarget as HTMLElement;
dragResizer = parent(currentTarget as HTMLElement) as HTMLElement;
const cSize = clientSize(element);
dragStartSize.w = cSize.w;
dragStartSize.h = cSize.h;
on(document, strSelectStartEvent, onSelectStart, { _passive: false });
on(document, strMouseTouchMoveEvent, resizerResize, { _passive: false });
on(document, strMouseTouchUpEvent, resizerResized, { _passive: false });
event.preventDefault();
event.stopPropagation();
}
},
{ _passive: false }
);
return {
addResizeListener(listener: ResizeListener) {
resizeListeners.push(listener);
},
};
};
+110
View File
@@ -0,0 +1,110 @@
function isEvent(obj: any): obj is Event {
return obj instanceof Event || !!obj.target;
}
// eslint-disable-next-line
const noop = <T>(): T => {
return {} as T;
};
const getSelectOptions = (selectElement: HTMLSelectElement) =>
Array.from(selectElement.options).map((option) => option.value);
export const generateSelectCallback =
(
targetElms: HTMLElement[] | HTMLElement | null,
callback: (
targetAffectedElm: HTMLElement,
possibleValues: string[],
selectedValue: string
) => any
) =>
(event: Event | HTMLSelectElement | null) => {
const target: HTMLSelectElement | null = isEvent(event)
? (event.target as HTMLSelectElement)
: event;
if (target) {
const selectedOption = target.value;
const selectOptions = getSelectOptions(target);
const elmsArr = Array.isArray(targetElms) ? targetElms : [targetElms];
elmsArr.forEach((elm) => {
if (elm) {
callback(elm, selectOptions, selectedOption);
}
});
}
};
export const generateClassChangeSelectCallback = (targetElms: HTMLElement[] | HTMLElement | null) =>
generateSelectCallback(targetElms, (targetAffectedElm, possibleValues, selectedValue) => {
possibleValues.forEach((clazz) => targetAffectedElm.classList.remove(clazz));
targetAffectedElm.classList.add(selectedValue);
});
export const selectOption = (
select: HTMLSelectElement | null,
selectedOption: string | number
): boolean => {
if (!select) {
return false;
}
const options = getSelectOptions(select);
const currValue = select.value;
if (selectedOption === currValue) {
return false;
}
if (typeof selectedOption === 'string' && options.includes(selectedOption)) {
select.value = selectedOption;
} else if (
typeof selectedOption === 'number' &&
options.length < selectedOption &&
selectedOption > -1
) {
select.selectedIndex = selectedOption;
}
let event;
if (typeof Event === 'function') {
event = new Event('change');
} else {
event = document.createEvent('Event');
event.initEvent('change', true, true);
}
select.dispatchEvent(event);
return true;
};
export const iterateSelect = async <T>(
select: HTMLSelectElement | null,
options?: {
filter?: (value: string, index: number, array: string[]) => boolean;
beforeEach?: () => T | Promise<T>;
check?: (input: T, selectedOptions: string) => void | Promise<void>;
afterEach?: () => void | Promise<void>;
}
) => {
if (select) {
const { beforeEach = noop, check = noop, afterEach = noop, filter } = options || {};
const selectOptions = getSelectOptions(select);
const selectOptionsReversed = getSelectOptions(select).reverse();
const iterateOptions = [...selectOptions, ...selectOptionsReversed].filter(
filter || (() => true)
);
for (let i = 0; i < iterateOptions.length; i++) {
const option = iterateOptions[i];
// eslint-disable-next-line
const beforeEachObj: T = await beforeEach();
if (selectOption(select, option)) {
// eslint-disable-next-line
await check(beforeEachObj, option);
// eslint-disable-next-line
await afterEach();
}
}
}
};
+33
View File
@@ -0,0 +1,33 @@
import { waitFor, waitForOptions } from '@testing-library/dom';
const getTestResultElm = () => document.getElementById('testResult');
export const setTestResult = (result: boolean | null) => {
const elm = getTestResultElm();
if (elm) {
if (typeof result === 'boolean') {
if (result) {
if (elm.getAttribute('class') === 'failed') {
return;
}
}
elm.setAttribute('class', result ? 'passed' : 'failed');
} else {
elm.removeAttribute('class');
}
}
};
export const testPassed = (): boolean => {
const elm = getTestResultElm();
return elm ? elm.getAttribute('class') === 'passed' : false;
};
export const waitForOrFailTest = <T>(callback: () => T | Promise<T>, options?: waitForOptions) =>
waitFor(callback, {
...options,
onTimeout(error: Error): Error {
setTestResult(false);
return error;
},
});
+4
View File
@@ -0,0 +1,4 @@
export const timeout = (ms: number) =>
new Promise((resolve) => {
setTimeout(resolve, ms);
});
+4
View File
@@ -0,0 +1,4 @@
export * from './select';
export * from './testResult';
export * from './timeout';
export * from './resize';
@@ -0,0 +1,79 @@
import { isString } from '../utils/types';
import { each } from '../utils/array';
import { keys } from '../utils/object';
type ClassContainingElement = Node | Element | false | null | undefined;
type ClassName = string | false | null | undefined;
const rnothtmlwhite = /[^\x20\t\r\n\f]+/g;
const classListAction = (
elm: ClassContainingElement,
className: ClassName,
action: (elmClassList: DOMTokenList, clazz: string) => boolean | void
): boolean => {
const classList = elm && (elm as Element).classList;
let clazz: string;
let i = 0;
let result = false;
if (classList && className && isString(className)) {
const classes: Array<string> = className.match(rnothtmlwhite) || [];
result = classes.length > 0;
while ((clazz = classes[i++])) {
result = !!action(classList, clazz) && result;
}
}
return result;
};
/**
* Check whether the given element has the given class name(s).
* @param elm The element.
* @param className The class name(s).
*/
export const hasClass = (elm: ClassContainingElement, className: ClassName): boolean =>
classListAction(elm, className, (classList, clazz) => classList.contains(clazz));
/**
* Removes the given class name(s) from the given element.
* @param elm The element.
* @param className The class name(s) which shall be removed. (separated by spaces)
*/
export const removeClass = (elm: ClassContainingElement, className: ClassName): void => {
classListAction(elm, className, (classList, clazz) => classList.remove(clazz));
};
/**
* Adds the given class name(s) to the given element.
* @param elm The element.
* @param className The class name(s) which shall be added. (separated by spaces)
* @returns A function which removes the added class name(s).
*/
export const addClass = (elm: ClassContainingElement, className: ClassName): (() => void) => {
classListAction(elm, className, (classList, clazz) => classList.add(clazz));
return removeClass.bind(0, elm, className);
};
/**
* Takes two className strings, compares them and returns the difference as array.
* @param classNameA ClassName A.
* @param classNameB ClassName B.
*/
export const diffClass = (classNameA: ClassName, classNameB: ClassName) => {
const classNameASplit = classNameA && classNameA.split(' ');
const classNameBSplit = classNameB && classNameB.split(' ');
const tempObj = {};
each(classNameASplit, (className) => {
tempObj[className] = 1;
});
each(classNameBSplit, (className) => {
if (tempObj[className]) {
delete tempObj[className];
} else {
tempObj[className] = 1;
}
});
return keys(tempObj);
};
@@ -0,0 +1,25 @@
import { each } from '../utils/array';
import { contents } from './traversal';
import { removeElements } from './manipulation';
/**
* Creates a div DOM node.
*/
export const createDiv = (classNames?: string): HTMLDivElement => {
const div = document.createElement('div');
if (classNames) {
div.setAttribute('class', classNames);
}
return div;
};
/**
* Creates DOM nodes modeled after the passed html string and returns the root dom nodes as a array.
* @param html The html string after which the DOM nodes shall be created.
*/
export const createDOM = (html: string): ReadonlyArray<Node> => {
const createdDiv = createDiv();
createdDiv.innerHTML = html.trim();
return each(contents(createdDiv), (elm) => removeElements(elm));
};
@@ -0,0 +1,68 @@
export interface WH<T = number> {
w: T;
h: T;
}
const elementHasDimensions = (elm: HTMLElement): boolean =>
!!(elm.offsetWidth || elm.offsetHeight || elm.getClientRects().length);
const zeroObj: WH = {
w: 0,
h: 0,
};
/**
* Returns the window inner- width and height.
*/
export const windowSize = (): WH => ({
w: window.innerWidth,
h: window.innerHeight,
});
/**
* Returns the scroll- width and height of the passed element. If the element is null the width and height values are 0.
* @param elm The element of which the scroll- width and height shall be returned.
*/
export const offsetSize = (elm: HTMLElement | null | undefined): WH =>
elm
? {
w: elm.offsetWidth,
h: elm.offsetHeight,
}
: zeroObj;
/**
* Returns the client- width and height of the passed element. If the element is null the width and height values are 0.
* @param elm The element of which the client- width and height shall be returned.
*/
export const clientSize = (elm: HTMLElement | false | null | undefined): WH =>
elm
? {
w: elm.clientWidth,
h: elm.clientHeight,
}
: zeroObj;
/**
* Returns the client- width and height of the passed element. If the element is null the width and height values are 0.
* @param elm The element of which the client- width and height shall be returned.
*/
export const scrollSize = (elm: HTMLElement | false | null | undefined): WH =>
elm
? {
w: elm.scrollWidth,
h: elm.scrollHeight,
}
: zeroObj;
/**
* Returns the BoundingClientRect of the passed element.
* @param elm The element of which the BoundingClientRect shall be returned.
*/
export const getBoundingClientRect = (elm: HTMLElement): DOMRect => elm.getBoundingClientRect();
/**
* Determines whether the passed element has any dimensions.
* @param elm The element.
*/
export const hasDimensions = (elm: HTMLElement | false | null | undefined): boolean =>
elm ? elementHasDimensions(elm as HTMLElement) : false;
@@ -0,0 +1,110 @@
import { isUndefined } from '../utils/types';
import { each, push, runEachAndClear } from '../utils/array';
let passiveEventsSupport: boolean;
const supportPassiveEvents = (): boolean => {
if (isUndefined(passiveEventsSupport)) {
passiveEventsSupport = false;
try {
/* eslint-disable */
// @ts-ignore
window.addEventListener(
'test',
null,
Object.defineProperty({}, 'passive', {
get() {
passiveEventsSupport = true;
},
})
);
/* eslint-enable */
} catch (e) {}
}
return passiveEventsSupport;
};
const splitEventNames = (eventNames: string) => eventNames.split(' ');
export interface OnOptions {
_capture?: boolean;
_passive?: boolean;
_once?: boolean;
}
/**
* Removes the passed event listener for the passed events with the passed options.
* @param target The element from which the listener shall be removed.
* @param eventNames The eventsnames for which the listener shall be removed.
* @param listener The listener which shall be removed.
* @param capture The options of the removed listener.
*/
export const off = <T extends Event = Event>(
target: EventTarget,
eventNames: string,
listener: (event: T) => any,
capture?: boolean
): void => {
each(splitEventNames(eventNames), (eventName) => {
target.removeEventListener(eventName, listener as EventListener, capture);
});
};
/**
* Adds the passed event listener for the passed eventnames with the passed options.
* @param target The element to which the listener shall be added.
* @param eventNames The eventsnames for which the listener shall be called.
* @param listener The listener which is called on the eventnames.
* @param options The options of the added listener.
*/
export const on = <T extends Event = Event>(
target: EventTarget,
eventNames: string,
listener: (event: T) => any,
options?: OnOptions
): (() => void) => {
const doSupportPassiveEvents = supportPassiveEvents();
const passive = (doSupportPassiveEvents && options && options._passive) ?? doSupportPassiveEvents;
const capture = (options && options._capture) || false;
const once = (options && options._once) || false;
const offListeners: (() => void)[] = [];
const nativeOptions: AddEventListenerOptions | boolean = doSupportPassiveEvents
? {
passive,
capture,
}
: capture;
each(splitEventNames(eventNames), (eventName) => {
const finalListener = (
once
? (evt: T) => {
target.removeEventListener(eventName, finalListener, capture);
listener && listener(evt);
}
: listener
) as EventListener;
push(offListeners, off.bind(null, target, eventName, finalListener, capture));
target.addEventListener(eventName, finalListener, nativeOptions);
});
return runEachAndClear.bind(0, offListeners);
};
/**
* Shorthand for the stopPropagation event Method.
* @param evt The event of which the stopPropagation method shall be called.
*/
export const stopPropagation = (evt: Event): void => evt.stopPropagation();
/**
* Shorthand for the preventDefault event Method.
* @param evt The event of which the preventDefault method shall be called.
*/
export const preventDefault = (evt: Event): void => evt.preventDefault();
/**
* Shorthand for the stopPropagation and preventDefault event Method.
* @param evt The event of which the stopPropagation and preventDefault methods shall be called.
*/
export const stopAndPrevent = (evt: Event): void =>
(stopPropagation(evt) as undefined) || (preventDefault(evt) as undefined);
@@ -0,0 +1,8 @@
export * from './class';
export * from './create';
export * from './dimensions';
export * from './events';
export * from './style';
export * from './manipulation';
export * from './offset';
export * from './traversal';
@@ -0,0 +1,110 @@
import { isArrayLike } from '../utils/types';
import { each, from } from '../utils/array';
import { parent } from './traversal';
type NodeCollection = ArrayLike<Node> | Node | false | null | undefined;
/**
* Inserts Nodes before the given preferredAnchor element.
* @param parentElm The parent of the preferredAnchor element or the element which shall be the parent of the inserted Nodes.
* @param preferredAnchor The element before which the Nodes shall be inserted or null if the elements shall be appended at the end.
* @param insertedElms The Nodes which shall be inserted.
*/
const before = (
parentElm: Node | false | null | undefined,
preferredAnchor: Node | false | null | undefined,
insertedElms: NodeCollection
): void => {
if (insertedElms && parentElm) {
let anchor: Node | false | null | undefined = preferredAnchor;
let fragment: DocumentFragment | Node | null | undefined;
if (isArrayLike(insertedElms)) {
fragment = document.createDocumentFragment();
// append all insertedElms to the fragment and if one of these is the anchor, change the anchor
each(insertedElms, (insertedElm) => {
if (insertedElm === anchor) {
anchor = insertedElm.previousSibling;
}
fragment!.appendChild(insertedElm);
});
} else {
fragment = insertedElms;
}
// if the preferred anchor isn't null set it to a valid anchor
if (preferredAnchor) {
if (!anchor) {
anchor = parentElm.firstChild;
} else if (anchor !== preferredAnchor) {
anchor = anchor.nextSibling;
}
}
parentElm.insertBefore(fragment, anchor || null);
}
};
/**
* Appends the given children at the end of the given Node.
* @param node The Node to which the children shall be appended.
* @param children The Nodes which shall be appended.
*/
export const appendChildren = (
node: Node | false | null | undefined,
children: NodeCollection
): void => {
before(node, null, children);
};
/**
* Prepends the given children at the start of the given Node.
* @param node The Node to which the children shall be prepended.
* @param children The Nodes which shall be prepended.
*/
export const prependChildren = (
node: Node | false | null | undefined,
children: NodeCollection
): void => {
before(node, node && node.firstChild, children);
};
/**
* Inserts the given Nodes before the given Node.
* @param node The Node before which the given Nodes shall be inserted.
* @param insertedNodes The Nodes which shall be inserted.
*/
export const insertBefore = (
node: Node | false | null | undefined,
insertedNodes: NodeCollection
): void => {
before(parent(node), node, insertedNodes);
};
/**
* Inserts the given Nodes after the given Node.
* @param node The Node after which the given Nodes shall be inserted.
* @param insertedNodes The Nodes which shall be inserted.
*/
export const insertAfter = (
node: Node | false | null | undefined,
insertedNodes: NodeCollection
): void => {
before(parent(node), node && node.nextSibling, insertedNodes);
};
/**
* Removes the given Nodes from their parent.
* @param nodes The Nodes which shall be removed.
*/
export const removeElements = (nodes: NodeCollection): void => {
if (isArrayLike(nodes)) {
each(from(nodes), (e) => removeElements(e));
} else if (nodes) {
const parentElm = parent(nodes);
if (parentElm) {
parentElm.removeChild(nodes);
}
}
};
@@ -0,0 +1,37 @@
import { getBoundingClientRect } from './dimensions';
export interface XY<T = number> {
x: T;
y: T;
}
const zeroObj: XY = {
x: 0,
y: 0,
};
/**
* Returns the offset- left and top coordinates of the passed element relative to the document. If the element is null the top and left values are 0.
* @param elm The element of which the offset- top and left coordinates shall be returned.
*/
export const absoluteCoordinates = (elm: HTMLElement | null | undefined): XY => {
const rect = elm ? getBoundingClientRect(elm) : 0;
return rect
? {
x: rect.left + window.pageYOffset,
y: rect.top + window.pageXOffset,
}
: zeroObj;
};
/**
* Returns the offset- left and top coordinates of the passed element. If the element is null the top and left values are 0.
* @param elm The element of which the offset- top and left coordinates shall be returned.
*/
export const offsetCoordinates = (elm: HTMLElement | null | undefined): XY =>
elm
? {
x: elm.offsetLeft,
y: elm.offsetTop,
}
: zeroObj;
@@ -0,0 +1,138 @@
import { each, keys } from '../utils';
import { isString, isNumber, isArray, isUndefined } from '../utils/types';
export interface TRBL {
t: number;
r: number;
b: number;
l: number;
}
type StyleObject<CustomCssProps = ''> = {
[Key in keyof CSSStyleDeclaration | (CustomCssProps extends string ? CustomCssProps : '')]?:
| string
| number;
};
const cssNumber = {
// animationiterationcount: 1,
// columncount: 1,
// fillopacity: 1,
// flexgrow: 1,
// flexshrink: 1,
// fontweight: 1,
// lineheight: 1,
// order: 1,
// orphans: 1,
// widows: 1,
// zoom: 1,
opacity: 1,
zindex: 1,
};
const parseToZeroOrNumber = (value: string, toFloat?: boolean): number => {
/* istanbul ignore next */
const num = toFloat ? parseFloat(value) : parseInt(value, 10);
// num === num means num is not NaN
/* istanbul ignore next */
return num === num ? num : 0; // eslint-disable-line no-self-compare
};
const adaptCSSVal = (prop: string, val: string | number): string | number =>
!cssNumber[prop.toLowerCase()] && isNumber(val) ? `${val}px` : val;
const getCSSVal = (elm: HTMLElement, computedStyle: CSSStyleDeclaration, prop: string): string =>
/* istanbul ignore next */
computedStyle != null
? computedStyle[prop] || computedStyle.getPropertyValue(prop)
: elm.style[prop];
const setCSSVal = (elm: HTMLElement, prop: string, val: string | number): void => {
try {
const { style: elmStyle } = elm;
if (!isUndefined(elmStyle[prop])) {
elmStyle[prop] = adaptCSSVal(prop, val);
} else {
elmStyle.setProperty(prop, val as string);
}
} catch (e) {}
};
/**
* Gets or sets the passed styles to the passed element.
* @param elm The element to which the styles shall be applied to / be read from.
* @param styles The styles which shall be set or read.
*/
export function style<CustomCssProps>(
elm: HTMLElement | false | null | undefined,
styles: StyleObject<CustomCssProps>
): void;
export function style(elm: HTMLElement | false | null | undefined, styles: string): string;
export function style(
elm: HTMLElement | false | null | undefined,
styles: Array<string> | string
): { [key: string]: string };
export function style<CustomCssProps>(
elm: HTMLElement | false | null | undefined,
styles: StyleObject<CustomCssProps> | Array<string> | string
): { [key: string]: string } | string | void {
const getSingleStyle = isString(styles);
const getStyles = isArray(styles) || getSingleStyle;
if (getStyles) {
let getStylesResult: string | Record<string, any> = getSingleStyle ? '' : {};
if (elm) {
const computedStyle: CSSStyleDeclaration = window.getComputedStyle(elm, null);
getStylesResult = getSingleStyle
? getCSSVal(elm, computedStyle, styles as string)
: (styles as Array<string>).reduce((result, key) => {
result[key] = getCSSVal(elm, computedStyle, key as string);
return result;
}, getStylesResult);
}
return getStylesResult;
}
elm && each(keys(styles), (key) => setCSSVal(elm, key, styles[key]));
}
/**
* Hides the passed element (display: none).
* @param elm The element which shall be hidden.
*/
export const hide = (elm: HTMLElement | false | null | undefined): void => {
style(elm, { display: 'none' });
};
/**
* Shows the passed element (display: block).
* @param elm The element which shall be shown.
*/
export const show = (elm: HTMLElement | false | null | undefined): void => {
style(elm, { display: 'block' });
};
/**
* Returns the top right bottom left values of the passed css property.
* @param elm The element of which the values shall be returned.
* @param propertyPrefix The css property prefix. (e.g. "border")
* @param propertySuffix The css property suffix. (e.g. "width")
*/
export const topRightBottomLeft = (
elm?: HTMLElement | false | null | undefined,
propertyPrefix?: string,
propertySuffix?: string
): TRBL => {
const finalPrefix = propertyPrefix ? `${propertyPrefix}-` : '';
const finalSuffix = propertySuffix ? `-${propertySuffix}` : '';
const top = `${finalPrefix}top${finalSuffix}`;
const right = `${finalPrefix}right${finalSuffix}`;
const bottom = `${finalPrefix}bottom${finalSuffix}`;
const left = `${finalPrefix}left${finalSuffix}`;
const result = style(elm, [top, right, bottom, left]);
return {
t: parseToZeroOrNumber(result[top]),
r: parseToZeroOrNumber(result[right]),
b: parseToZeroOrNumber(result[bottom]),
l: parseToZeroOrNumber(result[left]),
};
};
@@ -0,0 +1,120 @@
import { isElement } from '../utils/types';
import { push, from } from '../utils/array';
type InputElementType = Node | Element | Node | false | null | undefined;
type OutputElementType = Node | Element | null;
const elmPrototype = Element.prototype;
/**
* Find all elements with the passed selector, outgoing (and including) the passed element or the document if no element was provided.
* @param selector The selector which has to be searched by.
* @param elm The element from which the search shall be outgoing.
*/
const find = (selector: string, elm?: InputElementType): Element[] => {
const arr: Array<Element> = [];
const rootElm = elm ? (isElement(elm) ? elm : null) : document;
return rootElm ? push(arr, rootElm.querySelectorAll(selector)) : arr;
};
/**
* Find the first element with the passed selector, outgoing (and including) the passed element or the document if no element was provided.
* @param selector The selector which has to be searched by.
* @param elm The element from which the search shall be outgoing.
*/
const findFirst = (selector: string, elm?: InputElementType): OutputElementType => {
const rootElm = elm ? (isElement(elm) ? elm : null) : document;
return rootElm ? rootElm.querySelector(selector) : null;
};
/**
* Determines whether the passed element is matching with the passed selector.
* @param elm The element which has to be compared with the passed selector.
* @param selector The selector which has to be compared with the passed element. Additional selectors: ':visible' and ':hidden'.
*/
const is = (elm: InputElementType, selector: string): boolean => {
if (isElement(elm)) {
/* istanbul ignore next */
// eslint-disable-next-line
// @ts-ignore
const fn: (...args: any) => boolean = elmPrototype.matches || elmPrototype.msMatchesSelector;
return fn.call(elm, selector);
}
return false;
};
/**
* Returns the children (no text-nodes or comments) of the passed element which are matching the passed selector. An empty array is returned if the passed element is null.
* @param elm The element of which the children shall be returned.
* @param selector The selector which must match with the children elements.
*/
const children = (elm: InputElementType, selector?: string): ReadonlyArray<Element> => {
const childs: Array<Element> = [];
return isElement(elm)
? push(
childs,
from(elm.children).filter((child) => (selector ? is(child, selector) : child))
)
: childs;
};
/**
* Returns the childNodes (incl. text-nodes or comments etc.) of the passed element. An empty array is returned if the passed element is null.
* @param elm The element of which the childNodes shall be returned.
*/
const contents = (elm: InputElementType): ReadonlyArray<ChildNode> =>
elm ? from(elm.childNodes) : [];
/**
* Returns the parent element of the passed element, or null if the passed element is null.
* @param elm The element of which the parent element shall be returned.
*/
const parent = (elm: InputElementType): OutputElementType => (elm ? elm.parentElement : null);
const closest = (elm: InputElementType, selector: string): OutputElementType => {
if (isElement(elm)) {
const closestFn = elmPrototype.closest;
if (closestFn) {
return closestFn.call(elm, selector);
}
do {
if (is(elm, selector)) {
return elm as Element;
}
elm = parent(elm);
} while (elm);
}
return null;
};
/**
* Determines whether the given element lies between two selectors in the DOM.
* @param elm The element.
* @param highBoundarySelector The high boundary selector.
* @param deepBoundarySelector The deep boundary selector.
*/
const liesBetween = (
elm: InputElementType,
highBoundarySelector: string,
deepBoundarySelector: string
): boolean => {
const closestHighBoundaryElm = elm && closest(elm, highBoundarySelector);
const closestDeepBoundaryElm = elm && findFirst(deepBoundarySelector, closestHighBoundaryElm);
const deepBoundaryIsValid =
closest(closestDeepBoundaryElm, highBoundarySelector) === closestHighBoundaryElm;
return closestHighBoundaryElm && closestDeepBoundaryElm
? closestHighBoundaryElm === elm ||
closestDeepBoundaryElm === elm ||
(deepBoundaryIsValid &&
closest(closest(elm, deepBoundarySelector), highBoundarySelector) !==
closestHighBoundaryElm)
: false;
};
export { find, findFirst, is, children, contents, parent, liesBetween, closest };
@@ -0,0 +1,2 @@
export * from './dom';
export * from './utils';
@@ -0,0 +1,119 @@
import { isArrayLike, isString } from './types';
type PlainObject = Record<string, any>;
type RunEachItem = ((...args: any) => any | any[]) | null | undefined;
/**
* Iterates through a array or object
* @param arrayLikeOrObject The array or object through which shall be iterated.
* @param callback The function which is responsible for the iteration.
* If the function returns true its treated like a "continue" statement.
* If the function returns false its treated like a "break" statement.
*/
export function each<T>(
array: Array<T> | ReadonlyArray<T>,
callback: (value: T, indexOrKey: number, source: Array<T>) => boolean | unknown
): Array<T> | ReadonlyArray<T>;
export function each<T>(
array: Array<T> | ReadonlyArray<T> | false | null | undefined,
callback: (value: T, indexOrKey: number, source: Array<T>) => boolean | unknown
): Array<T> | ReadonlyArray<T> | false | null | undefined;
export function each<T>(
arrayLikeObject: ArrayLike<T>,
callback: (value: T, indexOrKey: number, source: ArrayLike<T>) => boolean | unknown
): ArrayLike<T>;
export function each<T>(
arrayLikeObject: ArrayLike<T> | false | null | undefined,
callback: (value: T, indexOrKey: number, source: ArrayLike<T>) => boolean | unknown
): ArrayLike<T> | false | null | undefined;
export function each(
obj: PlainObject,
callback: (value: any, indexOrKey: string, source: PlainObject) => boolean | unknown
): PlainObject;
export function each(
obj: PlainObject | false | null | undefined,
callback: (value: any, indexOrKey: string, source: PlainObject) => boolean | unknown
): PlainObject | false | null | undefined;
export function each<T>(
source: Array<T> | ArrayLike<T> | ReadonlyArray<T> | PlainObject | false | null | undefined,
callback: (value: T, indexOrKey: any, source: any) => boolean | unknown
): Array<T> | ArrayLike<T> | ReadonlyArray<T> | PlainObject | false | null | undefined {
if (isArrayLike(source)) {
for (let i = 0; i < source.length; i++) {
if (callback(source[i], i, source) === false) {
break;
}
}
} else if (source) {
// cant use support func keys here due to circular dep
each(Object.keys(source), (key) => callback(source[key], key, source));
}
return source;
}
/**
* Returns the index of the given inside the given array or -1 if the given item isn't part of the given array.
* @param arr The array.
* @param item The item.
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.
*/
export const indexOf = <T = any>(arr: T[], item: T, fromIndex?: number): number =>
arr.indexOf(item, fromIndex);
/**
* Pushesh all given items into the given array and returns it.
* @param array The array the items shall be pushed into.
* @param items The items which shall be pushed into the array.
*/
export const push = <T>(array: T[], items: T | ArrayLike<T>, arrayIsSingleItem?: boolean): T[] => {
!arrayIsSingleItem && !isString(items) && isArrayLike(items)
? Array.prototype.push.apply(array, items as T[])
: array.push(items as T);
return array;
};
/**
* Creates a shallow-copied Array instance from an array-like or iterable object.
* @param arr The object from which the array instance shall be created.
*/
export const from = <T = any>(arr?: ArrayLike<T> | Set<T>) => {
const original = Array.from;
const result: T[] = [];
if (original && arr) {
return original(arr);
}
if (arr instanceof Set) {
arr.forEach((value) => {
push(result, value);
});
} else {
each(arr, (elm) => {
push(result, elm);
});
}
return result;
};
/**
* Check whether the passed array is empty.
* @param array The array which shall be checked.
*/
export const isEmptyArray = (array: any[] | null | undefined): boolean =>
!!array && array.length === 0;
/**
* Calls all functions in the passed array/set of functions.
* @param arr The array filled with function which shall be called.
* @param args The args with which each function is called.
* @param keep True when the Set / array should not be cleared afterwards, false otherwise.
*/
export const runEachAndClear = (arr: RunEachItem[], args?: any[], keep?: boolean): void => {
// eslint-disable-next-line prefer-spread
const runFn = (fn: RunEachItem) => fn && fn.apply(undefined, args || []);
each(arr, runFn);
!keep && ((arr as any[]).length = 0);
};
@@ -0,0 +1,3 @@
export * from './array';
export * from './object';
export * from './types';
@@ -0,0 +1,104 @@
import { isArray, isFunction, isPlainObject, isNull } from './types';
import { each } from './array';
/**
* Determines whether the passed object has a property with the passed name.
* @param obj The object.
* @param prop The name of the property.
*/
export const hasOwnProperty = (obj: any, prop: string | number | symbol): boolean =>
Object.prototype.hasOwnProperty.call(obj, prop);
/**
* Returns the names of the enumerable string properties and methods of an object.
* @param obj The object of which the properties shall be returned.
*/
export const keys = (obj: any): Array<string> => (obj ? Object.keys(obj) : []);
type AssignDeep = {
<T, U>(target: T, object1: U): T & U;
<T, U, V>(target: T, object1: U, object2: V): T & U & V;
<T, U, V, W>(target: T, object1: U, object2: V, object3: W): T & U & V & W;
<T, U, V, W, X>(target: T, object1: U, object2: V, object3: W, object4: X): T & U & V & W & X;
<T, U, V, W, X, Y>(target: T, object1: U, object2: V, object3: W, object4: X, object5: Y): T &
U &
V &
W &
X &
Y;
<T, U, V, W, X, Y, Z>(
target: T,
object1?: U,
object2?: V,
object3?: W,
object4?: X,
object5?: Y,
object6?: Z
): T & U & V & W & X & Y & Z;
};
// https://github.com/jquery/jquery/blob/master/src/core.js#L116
export const assignDeep: AssignDeep = <T, U, V, W, X, Y, Z>(
target: T,
object1?: U,
object2?: V,
object3?: W,
object4?: X,
object5?: Y,
object6?: Z
): T & U & V & W & X & Y & Z => {
const sources: Array<any> = [object1, object2, object3, object4, object5, object6];
// Handle case when target is a string or something (possible in deep copy)
if ((typeof target !== 'object' || isNull(target)) && !isFunction(target)) {
target = {} as T;
}
each(sources, (source) => {
// Extend the base object
each(keys(source), (key) => {
const copy: any = source[key];
// Prevent Object.prototype pollution
// Prevent never-ending loop
if (target === copy) {
return true;
}
const copyIsArray = isArray(copy);
// Recurse if we're merging plain objects or arrays
if (copy && (isPlainObject(copy) || copyIsArray)) {
const src = target[key];
let clone: any = src;
// Ensure proper type for the source value
if (copyIsArray && !isArray(src)) {
clone = [];
} else if (!copyIsArray && !isPlainObject(src)) {
clone = {};
}
// Never move original objects, clone them
target[key] = assignDeep(clone, copy) as any;
} else {
target[key] = copy;
}
});
});
// Return the modified object
return target as any;
};
/**
* Returns true if the given object is empty, false otherwise.
* @param obj The Object.
*/
export const isEmptyObject = (obj: any): boolean => {
/* eslint-disable no-restricted-syntax, guard-for-in */
// eslint-disable-next-line no-unreachable-loop
for (const name in obj) return false;
return true;
/* eslint-enable */
};
@@ -0,0 +1,97 @@
type PlainObject<T = any> = Record<string, T>;
const ElementNodeType = Node.ELEMENT_NODE;
const { toString, hasOwnProperty } = Object.prototype;
export const isUndefined = (obj: any): obj is undefined => obj === undefined;
export const isNull = (obj: any): obj is null => obj === null;
export const type = (obj: any): string =>
isUndefined(obj) || isNull(obj)
? `${obj}`
: toString
.call(obj)
.replace(/^\[object (.+)\]$/, '$1')
.toLowerCase();
export const isNumber = (obj: any): obj is number => typeof obj === 'number';
export const isString = (obj: any): obj is string => typeof obj === 'string';
export const isBoolean = (obj: any): obj is boolean => typeof obj === 'boolean';
export const isFunction = (obj: any): obj is (...args: any[]) => any => typeof obj === 'function';
export const isArray = <T = any>(obj: any): obj is Array<T> => Array.isArray(obj);
export const isObject = (obj: any): boolean =>
typeof obj === 'object' && !isArray(obj) && !isNull(obj);
/**
* Returns true if the given object is array like, false otherwise.
* @param obj The Object
*/
export const isArrayLike = <T extends PlainObject = any>(obj: any): obj is ArrayLike<T> => {
const length = !!obj && obj.length;
const lengthCorrectFormat = isNumber(length) && length > -1 && length % 1 == 0; // eslint-disable-line eqeqeq
return isArray(obj) || (!isFunction(obj) && lengthCorrectFormat)
? length > 0 && isObject(obj)
? length - 1 in obj
: true
: false;
};
/**
* Returns true if the given object is a "plain" (e.g. { key: value }) object, false otherwise.
* @param obj The Object.
*/
export const isPlainObject = <T = any>(obj: any): obj is PlainObject<T> => {
if (!obj || !isObject(obj) || type(obj) !== 'object') return false;
let key;
const cstr = 'constructor';
const ctor = obj[cstr];
const ctorProto = ctor && ctor.prototype;
const hasOwnConstructor = hasOwnProperty.call(obj, cstr);
const hasIsPrototypeOf = ctorProto && hasOwnProperty.call(ctorProto, 'isPrototypeOf');
if (ctor && !hasOwnConstructor && !hasIsPrototypeOf) {
return false;
}
/* eslint-disable no-restricted-syntax */
for (key in obj) {
/**/
}
/* eslint-enable */
return isUndefined(key) || hasOwnProperty.call(obj, key);
};
/**
* Checks whether the given object is a HTMLElement.
* @param obj The object which shall be checked.
*/
export const isHTMLElement = (obj: any): obj is HTMLElement => {
const instanceofObj = HTMLElement;
return obj
? instanceofObj
? obj instanceof instanceofObj
: obj.nodeType === ElementNodeType
: false;
};
/**
* Checks whether the given object is a Element.
* @param obj The object which shall be checked.
*/
export const isElement = (obj: any): obj is Element => {
const instanceofObj = Element;
return obj
? instanceofObj
? obj instanceof instanceofObj
: obj.nodeType === ElementNodeType
: false;
};
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "@~local/tsconfig",
"compilerOptions": {
"outDir": "./dist/",
"baseUrl": "./src/",
"typeRoots": []
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"private": true,
"name": "@~local/config",
"exports": {
"./resolve": "./src/resolve.json",
"./playwright": "./src/playwright.js",
"./playwright.coverage": "./src/playwright.coverage.js",
"./jest": "./src/jest.js",
"./babel": "./src/babel.js",
"./full-coverage": "./src/full-coverage.js"
},
"version": "0.0.0"
}
+26
View File
@@ -0,0 +1,26 @@
module.exports = function (api) {
api.cache.using(() => process.env.NODE_ENV);
const isJest = api.caller((caller) => !!(caller && caller.name === 'babel-jest'));
if (isJest) {
return {
plugins: [
'@babel/plugin-transform-modules-commonjs',
['@babel/plugin-proposal-private-methods', { loose: false }],
],
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
'@babel/preset-typescript',
],
};
}
return {};
};
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
tmpCoverageDirectory: './.coverage/.nycFull',
coverageDirectory: './.coverage/full',
};
+31
View File
@@ -0,0 +1,31 @@
const path = require('path');
const resolve = require('./resolve');
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
module.exports = {
coverageDirectory: './.coverage/jest',
projects: [
{
displayName: 'node',
testMatch: ['**/tests/jest-node/**/*.test.[jt]s?(x)'],
testEnvironment: 'node',
clearMocks: true,
moduleDirectories: resolve.directories,
moduleFileExtensions: resolve.extensions.map((ext) => ext.replace(/\./, '')),
testPathIgnorePatterns: ['\\\\node_modules\\\\'],
setupFilesAfterEnv: [path.resolve(__dirname, './jest.setup.js')],
},
{
displayName: 'jsdom',
testMatch: ['**/tests/jest-jsdom/**/*.test.[jt]s?(x)'],
testEnvironment: 'jsdom',
clearMocks: true,
moduleDirectories: resolve.directories,
moduleFileExtensions: resolve.extensions.map((ext) => ext.replace(/\./, '')),
testPathIgnorePatterns: ['\\\\node_modules\\\\'],
setupFilesAfterEnv: [path.resolve(__dirname, './jest.setup.js')],
},
],
};
+5
View File
@@ -0,0 +1,5 @@
// remove jsdom warning for not implemented second argument for window.getComputedStyle
try {
const cmptdStyle = window.getComputedStyle;
window.getComputedStyle = (a) => cmptdStyle(a);
} catch {}
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
tmpCoverageDirectory: './.coverage/.nycPlaywright',
coverageDirectory: './.coverage/playwright',
};
+35
View File
@@ -0,0 +1,35 @@
const { devices } = require('@playwright/test');
module.exports = {
testMatch: /.*\/tests\/playwright\/.*\.test\.[jt]sx?/,
timeout: 10 * 60 * 1000,
actionTimeout: 300,
navigationTimeout: 1000,
retries: 0,
workers: 4,
projects: [
{
name: 'Chromium',
use: {
...devices['Desktop Chromium'],
headless: false,
},
},
/*
{
name: 'Firefox',
use: {
...devices['Desktop Firefox'],
headless: false,
},
},
{
name: 'Safari',
use: {
...devices['Desktop Safari'],
headless: false,
},
},
*/
],
};
+5
View File
@@ -0,0 +1,5 @@
{
"extensions": [".json", ".js", "jsx", ".mjs", ".ts", ".tsx"],
"directories": ["node_modules", "src"],
"styleExtensions": [".scss", ".sass", ".css"]
}
@@ -0,0 +1,59 @@
#!/usr/bin/env node
const fs = require('fs');
const { execSync } = require('child_process');
const { join, dirname, basename } = require('path');
const { coverageDirectory, tmpCoverageDirectory } = require('@~local/config/full-coverage');
const { coverageDirectory: playwrightCoverage } = require('@~local/config/playwright.coverage');
const { coverageDirectory: jestCoverage } = require('@~local/config/jest');
const reportFileName = 'coverage-final.json';
const copyReportFile = (path) => {
if (fs.existsSync(path)) {
if (!fs.existsSync(tmpCoverageDirectory)) {
fs.mkdirSync(tmpCoverageDirectory);
}
fs.copyFileSync(
path,
join(tmpCoverageDirectory, `${basename(dirname(path))}_${Date.now()}.json`)
);
return true;
}
return false;
};
const generateFullCoverage = async () => {
const copiedPlaywright = copyReportFile(join(playwrightCoverage, reportFileName));
const copiedJest = copyReportFile(join(jestCoverage, reportFileName));
if (copiedPlaywright || copiedJest) {
const mergeDestination = join(tmpCoverageDirectory, `full_${Date.now()}.json`);
execSync(`nyc merge ${tmpCoverageDirectory} ${mergeDestination}`);
const files = fs.readdirSync(tmpCoverageDirectory);
files.forEach((file) => {
const filePath = join(tmpCoverageDirectory, file);
if (filePath !== mergeDestination) {
fs.rmSync(filePath);
}
});
execSync(
`nyc report --reporter=lcov --reporter=clover --reporter=json --report-dir=${coverageDirectory} --temp-dir=${tmpCoverageDirectory}`
/* { stdio: 'inherit' } */
);
fs.rmSync(tmpCoverageDirectory, { recursive: true });
}
};
(async () => {
try {
await generateFullCoverage();
} catch (e) {
// console.error(`Full coverage couldn't be generated.`, e);
if (fs.existsSync(tmpCoverageDirectory)) {
fs.rmSync(tmpCoverageDirectory, { recursive: true });
}
}
})();
+8
View File
@@ -0,0 +1,8 @@
{
"private": true,
"name": "@~local/full-coverage",
"bin": {
"full-coverage": "./bin/generateFullCoverage.js"
},
"version": "0.0.0"
}
@@ -0,0 +1,36 @@
#!/usr/bin/env node
const fs = require('fs');
const { execSync } = require('child_process');
const { join } = require('path');
const { coverageDirectory, tmpCoverageDirectory } = require('@~local/config/playwright.coverage');
const mergeCoverage = async () => {
if (fs.existsSync(tmpCoverageDirectory)) {
const mergeDestination = join(tmpCoverageDirectory, `merged_${Date.now()}.json`);
execSync(`nyc merge ${tmpCoverageDirectory} ${mergeDestination}`);
const files = fs.readdirSync(tmpCoverageDirectory);
files.forEach((file) => {
const filePath = join(tmpCoverageDirectory, file);
if (filePath !== mergeDestination) {
fs.rmSync(filePath);
}
});
execSync(
`nyc report --reporter=lcov --reporter=text --reporter=clover --reporter=json --report-dir=${coverageDirectory} --temp-dir=${tmpCoverageDirectory}`,
{ stdio: 'inherit' }
);
fs.rmSync(tmpCoverageDirectory, { recursive: true });
}
};
(async () => {
try {
await mergeCoverage();
} catch (e) {
// console.error(`Playwright coverage couldn't be merged.`, e);
if (fs.existsSync(tmpCoverageDirectory)) {
fs.rmSync(tmpCoverageDirectory, { recursive: true });
}
}
})();
+4
View File
@@ -0,0 +1,4 @@
declare module '@~local/playwright-tooling' {
export function playwrightRollup(): void;
export function expectSuccess(page: any): void;
}
+10
View File
@@ -0,0 +1,10 @@
{
"private": true,
"name": "@~local/playwright-tooling",
"main": "./src/index.js",
"bin": {
"playwright-merge-coverage": "./bin/mergeCoverage.js"
},
"types": "index.d.ts",
"version": "0.0.0"
}
@@ -0,0 +1,22 @@
const fs = require('fs');
const { basename, dirname, join } = require('path');
const v8toIstanbul = require('v8-to-istanbul');
const { tmpCoverageDirectory } = require('@~local/config/playwright.coverage');
module.exports = async (coverageOutputDir, sourceDir, coverage, testfile) => {
// eslint-disable-next-line no-restricted-syntax
for (const entry of coverage) {
process.chdir(sourceDir);
const converter = v8toIstanbul('', 0, { source: entry.source });
// eslint-disable-next-line no-await-in-loop
await converter.load();
converter.applyCoverage(entry.functions);
const coveragePath = join(
coverageOutputDir,
`${tmpCoverageDirectory}/${basename(dirname(testfile))}_${Date.now()}.json`
);
fs.mkdirSync(dirname(coveragePath), { recursive: true });
fs.writeFileSync(coveragePath, JSON.stringify(converter.toIstanbul()));
}
};
@@ -0,0 +1,8 @@
const { expect } = require('@playwright/test');
const resultSelector = '#testResult';
module.exports = async (page) => {
await page.locator(resultSelector).waitFor({ state: 'visible', timeout: 10 * 60 * 1000 }); // 10mins
await expect(page.locator(resultSelector)).toHaveClass('passed', { timeout: 500 });
}
+7
View File
@@ -0,0 +1,7 @@
const playwrightRollup = require('./playwrightRollup');
const expectSuccess = require('./expectSuccess');
module.exports = {
playwrightRollup,
expectSuccess,
};
@@ -0,0 +1,82 @@
const { dirname } = require('path');
const { rollup, watch: rollupWatch } = require('rollup');
const { test } = require('@playwright/test');
const createPlaywrightRollupConfig = require('@~local/rollup/playwright');
const collectCoverage = require('./collectCoverage');
const createRollupBundle = async (testDir, watch = false) => {
let server;
const onListening = (srv) => {
server = srv;
};
const config = await createPlaywrightRollupConfig(testDir, 'dev', onListening);
if (watch) {
const watcher = rollupWatch(config);
let outputPath = '';
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
watcher.on('event', ({ code, error, result, output }) => {
if (code === 'ERROR') {
console.log('Error:', error); // eslint-disable-line
}
if (code === 'BUNDLE_END') {
outputPath = output[0];
if (result && result.close) {
result.close();
}
}
if (code === 'END') {
/*
console.log('Watching for changes, press ENTER to continue.'); // eslint-disable-line
console.log(''); // eslint-disable-line
*/
resolve();
}
});
});
const { address, port } = server.address();
return {
url: `${address}:${port}`,
output: outputPath,
close: () => {
server.close();
watcher.close();
},
};
}
const bundle = await rollup(config);
await bundle.write(config.output);
};
module.exports = () => {
const originalCwd = process.cwd();
let url;
let close;
let output;
// eslint-disable-next-line no-empty-pattern
test.beforeAll(async ({}, { file, config }) => {
({ close, url, output } = await createRollupBundle(dirname(file), true, config.quiet));
});
test.beforeEach(async ({ page, browserName }, { config }) => {
await page.goto(url);
if (browserName === 'chromium' && config.quiet) {
await page.coverage.startJSCoverage();
}
});
test.afterEach(async ({ page, browserName }, { file, config }) => {
if (browserName === 'chromium' && config.quiet) {
const coverage = await page.coverage.stopJSCoverage();
await collectCoverage(originalCwd, dirname(output), coverage, file);
}
});
test.afterAll(() => {
close();
});
};
+9
View File
@@ -0,0 +1,9 @@
{
"private": true,
"name": "@~local/rollup",
"exports": {
".": "./src/createRollupConfig.js",
"./playwright": "./src/playwright/createPlaywrightRollupConfig.js"
},
"version": "0.0.0"
}
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
loose: true,
bugfixes: true,
targets: {
esmodules: true,
},
},
],
],
};
+16
View File
@@ -0,0 +1,16 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
loose: true,
targets: {
firefox: '54',
chrome: '58',
ie: '11',
esmodules: false,
},
},
],
],
};
+135
View File
@@ -0,0 +1,135 @@
/* eslint-disable no-console */
/* eslint-disable import/no-dynamic-require */
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const resolve = require('@~local/config/resolve');
const defaultOptions = require('./defaultOptions');
const pipelineBuild = require('./pipeline.build');
const pipelineDev = require('./pipeline.dev');
const pipelineStyles = require('./pipeline.styles');
const pipelineTypes = require('./pipeline.types');
const workspaceRoot = path.dirname(execSync('npm root').toString());
const pkg = require(`${workspaceRoot}/package.json`);
const appendExtension = (file) =>
path.extname(file) === ''
? file + (resolve.extensions.find((ext) => fs.existsSync(path.resolve(`${file}${ext}`))) || '')
: file;
const normalizePath = (pathName) =>
pathName ? pathName.split(path.sep).join(path.posix.sep) : pathName;
const resolvePath = (basePath, pathToResolve, appendExt) => {
const result = pathToResolve
? path.isAbsolute(pathToResolve)
? pathToResolve
: path.resolve(basePath, pathToResolve)
: null;
return normalizePath(result && appendExt ? appendExtension(result) : result);
};
const mergeAndResolveOptions = (userOptions) => {
const {
mode: defaultMode,
paths: defaultPaths,
versions: defaultVersions,
alias: defaultAlias,
rollup: defaultRollup,
extractStyles: defaultExtractStyles,
extractTypes: defaultExtractTypes,
verbose: defaultVerbose,
banner: defaultBanner,
} = defaultOptions;
const {
project,
mode: rawMode,
paths: rawPaths = {},
alias: rawAlias = {},
rollup: rawRollup = {},
versions: rawVersions,
extractStyles: rawExtractStyles,
extractTypes: rawExtractTypes,
verbose: rawVerbose,
banner: rawBanner,
} = userOptions;
const projectPath = process.cwd();
const workspaces = pkg.workspaces
.map((pattern) => glob.sync(pattern, { cwd: workspaceRoot }))
.flat();
const mergedOptions = {
project: project || path.basename(projectPath),
mode: rawMode || defaultMode,
extractStyles: rawExtractStyles ?? defaultExtractStyles,
extractTypes: rawExtractTypes ?? defaultExtractTypes,
verbose: rawVerbose ?? defaultVerbose,
banner: rawBanner ?? defaultBanner,
versions: rawVersions ?? defaultVersions,
paths: {
...defaultPaths,
...rawPaths,
},
alias: {
...defaultAlias,
...(typeof rawAlias === 'function'
? rawAlias(workspaceRoot, workspaces, resolvePath)
: rawAlias),
},
rollup: {
...defaultRollup,
...rawRollup,
output: {
...defaultRollup.output,
...(rawRollup.output || {}),
},
},
};
const { dist, types, styles } = mergedOptions.paths;
mergedOptions.paths.dist = resolvePath(projectPath, dist);
mergedOptions.paths.types = resolvePath(projectPath, types);
mergedOptions.paths.styles = resolvePath(projectPath, styles);
mergedOptions.rollup.input = resolvePath(projectPath, mergedOptions.rollup.input, true);
mergedOptions.rollup.output = {
...(mergedOptions.rollup.output || {}),
name: mergedOptions.rollup.output?.name || mergedOptions.project,
file: mergedOptions.rollup.output?.file || mergedOptions.project.toLocaleLowerCase(),
};
return mergedOptions;
};
const createConfig = (userOptions = {}) => {
const options = mergeAndResolveOptions(userOptions);
const { project, mode, extractTypes, extractStyles, verbose } = options;
const isBuild = mode === 'build';
let result;
if (isBuild) {
const styles = extractStyles && pipelineStyles(resolve, options);
const types = extractTypes && pipelineTypes(resolve, options);
const js = pipelineBuild(resolve, options);
result = [styles, types, js].flat().filter((build) => !!build);
} else {
result = [pipelineDev(resolve, options)];
}
if (verbose) {
result[0].plugins.push({
name: 'PROJECT',
buildStart() {
console.log('');
console.log('PROJECT : ', project);
console.log('OPTIONS : ', options);
},
});
}
return result;
};
module.exports = createConfig;
+35
View File
@@ -0,0 +1,35 @@
module.exports = {
project: null,
mode: 'build',
verbose: false,
banner: null,
paths: {
dist: './dist',
types: './types',
styles: './styles',
},
versions: [
{
format: 'cjs',
generatedCode: 'es2015',
outputSuffix: '.cjs',
minifiedVersion: true,
},
{
format: 'esm',
generatedCode: 'es2015',
outputSuffix: '.esm',
minifiedVersion: true,
},
],
extractStyles: false,
extractTypes: false,
alias: {},
rollup: {
input: './src/index',
output: {
sourcemap: false,
exports: 'auto',
},
},
};
+78
View File
@@ -0,0 +1,78 @@
const path = require('path');
const { terser: rollupTerser } = require('rollup-plugin-terser');
const {
rollupBabel,
rollupTs,
rollupCommonjs,
rollupResolve,
rollupAlias,
rollupScss,
rollupLicense,
} = require('./pipeline.common.plugins');
const moduleFormats = ['es', 'esm', 'module'];
const createMinifiedOutput = (baseOutput) => ({
...baseOutput,
compact: true,
file: baseOutput.file.replace('.js', '.min.js'),
sourcemap: false,
plugins: [
...(baseOutput.plugins || []),
rollupTerser({
ecma: baseOutput.generatedCode === 'es2015' ? 2015 : 5,
safari10: true,
compress: {
evaluate: false,
module: moduleFormats.includes(baseOutput.format),
passes: 3,
},
}),
],
});
module.exports = (resolve, options) => {
const { rollup, paths, versions, alias, extractStyles, banner } = options;
const { output: rollupOutput, input, plugins = [], ...rollupOptions } = rollup;
const { name, file, globals, exports, sourcemap: rawSourcemap, ...outputConfig } = rollupOutput;
const { dist: distPath } = paths;
const sourcemap = rawSourcemap;
return versions
.map(({ format, generatedCode, file: filePathOverride, outputSuffix, minifiedVersion }) => {
const needsGlobals = format === 'umd' || format === 'iife';
const filePath = path.resolve(distPath, `${file}${outputSuffix || ''}.js`);
const baseOutput = {
...outputConfig,
...(needsGlobals && {
name,
globals,
exports,
}),
sourcemap,
format,
generatedCode,
file: typeof filePathOverride === 'function' ? filePathOverride(filePath) : filePath,
};
const output = [baseOutput, minifiedVersion && createMinifiedOutput(baseOutput)].filter(
Boolean
);
return {
input,
output,
...rollupOptions,
plugins: [
rollupLicense(banner, sourcemap),
rollupAlias(alias),
rollupScss(resolve, sourcemap, extractStyles, false),
rollupTs(input),
rollupResolve(resolve),
rollupCommonjs(sourcemap, resolve),
rollupBabel(resolve, generatedCode === 'es2015'),
...plugins,
].filter(Boolean),
};
})
.flat();
};
+122
View File
@@ -0,0 +1,122 @@
const path = require('path');
const sass = require('sass');
const postcss = require('postcss');
const cssnano = require('cssnano');
const autoprefixer = require('autoprefixer');
const { nodeResolve: rollupPluginResolve } = require('@rollup/plugin-node-resolve');
const { babel: rollupBabelInputPlugin } = require('@rollup/plugin-babel');
const rollupPluginScss = require('rollup-plugin-scss');
const rollupPluginIgnoreImport = require('rollup-plugin-ignore-import');
const rollupPluginCommonjs = require('@rollup/plugin-commonjs');
const rollupPluginAlias = require('@rollup/plugin-alias');
const rollupPluginTs = require('rollup-plugin-typescript2');
const { default: rollupPluginEsBuild } = require('rollup-plugin-esbuild');
const rollupPluginLicense = require('rollup-plugin-license');
const babelConfigEs5 = require('./babel.config.es5');
const babelConfigEs6 = require('./babel.config.es2015');
const normalizePath = (pathName) =>
pathName ? pathName.split(path.sep).join(path.posix.sep) : pathName;
module.exports = {
rollupAlias: (aliasEntries) =>
rollupPluginAlias({
entries: aliasEntries,
}),
rollupCommonjs: (sourcemap, resolve) =>
rollupPluginCommonjs({
sourceMap: sourcemap,
extensions: resolve.extensions,
}),
rollupResolve: (resolve, resolveOnly) =>
rollupPluginResolve({
mainFields: ['browser', 'umd:main', 'module', 'main'],
moduleDirectories: resolve.directories,
extensions: resolve.extensions,
ignoreSideEffectsForRoot: true,
...(resolveOnly ? { resolveOnly } : {}),
}),
rollupScss: (resolve, sourceMap, extract, output, banner, minified) => {
if (extract) {
return output
? rollupPluginScss({
output,
sourceMap,
sass,
prefix: banner
? `/*! \r\n${banner
.replace(/\r\n/g, '\r')
.replace(/\n/g, '\r')
.split(/\r/)
.map((line) => ` * ${line}\r\n`)
.join('')} */`
: undefined,
processor: () => postcss([autoprefixer()].concat(minified ? cssnano() : [])),
})
: rollupPluginIgnoreImport({
extensions: resolve.styleExtensions,
body: 'export default undefined;',
});
}
},
rollupEsBuild: () =>
rollupPluginEsBuild({
include: /\.[jt]sx?$/,
target: 'es6',
}),
rollupBabel: (resolve, es6) =>
rollupBabelInputPlugin({
...(es6 ? babelConfigEs6 : babelConfigEs5),
assumptions: {
enumerableModuleMeta: false,
constantReexports: true,
iterableIsArray: true,
objectRestNoSymbols: true,
noNewArrows: true,
noClassCalls: true,
ignoreToPrimitiveHint: true,
ignoreFunctionLength: true,
// privateFieldsAsProperties: true,
// setPublicClassFields: true,
setSpreadProperties: true,
pureGetters: true,
},
plugins: [
'@babel/plugin-transform-runtime',
['@babel/plugin-proposal-class-properties', { loose: true }],
['@babel/plugin-proposal-private-methods', { loose: true }],
],
babelHelpers: 'runtime',
shouldPrintComment: (comment) => /@__PURE__/.test(comment),
caller: {
name: 'babel-rollup-build',
},
extensions: resolve.extensions,
}),
rollupTs: (input, declaration) =>
rollupPluginTs({
tsconfigOverride: {
compilerOptions: {
declaration,
emitDeclarationOnly: declaration,
declarationMap: declaration,
},
// files to include / exclude from typescript .d.ts generation
include: [`${normalizePath(path.dirname(path.resolve(input)))}/**/*`],
exclude: ['node_modules', '**/node_modules/*', '*.d.ts', '**/*.d.ts'],
},
clean: true,
// files to include / exclude from the plugin
include: ['*.ts+(|x)', '**/*.ts+(|x)'],
exclude: ['node_modules', '**/node_modules/*', '*.d.ts', '**/*.d.ts'],
}),
rollupLicense: (content, sourceMap) =>
content &&
rollupPluginLicense({
sourcemap: sourceMap,
banner: {
content,
commentStyle: 'ignored',
},
}),
};
+38
View File
@@ -0,0 +1,38 @@
const path = require('path');
const {
rollupEsBuild,
rollupCommonjs,
rollupResolve,
rollupAlias,
rollupScss,
} = require('./pipeline.common.plugins');
module.exports = (resolve, options) => {
const { rollup, paths, alias, extractStyles } = options;
const { output: rollupOutput, input, plugins = [], ...rollupOptions } = rollup;
const { file, sourcemap: rawSourcemap, ...outputConfig } = rollupOutput;
const { dist: distPath } = paths;
const sourcemap = rawSourcemap;
const output = {
...outputConfig,
sourcemap: true,
format: 'esm',
generatedCode: 'es2015',
file: path.resolve(distPath, `${file}.js`),
};
return {
input,
output,
...rollupOptions,
plugins: [
rollupAlias(alias),
rollupScss(resolve, sourcemap, extractStyles, false),
rollupEsBuild(),
rollupCommonjs(sourcemap, resolve),
rollupResolve(resolve),
...plugins,
].filter(Boolean),
};
};
+46
View File
@@ -0,0 +1,46 @@
const path = require('path');
const {
rollupAlias,
rollupResolve,
rollupScss,
rollupEsBuild,
} = require('./pipeline.common.plugins');
module.exports = (resolve, options) => {
const { rollup, alias, paths, banner, extractStyles } = options;
const { output: rollupOutput, input } = rollup;
const { file, sourcemap } = rollupOutput;
const { styles: stylesPath } = paths;
const ogWrite = process.stdout.write;
const pipeline = (cssFilename, minified) => ({
input,
plugins: [
rollupAlias(alias),
rollupScss(
resolve,
sourcemap && !minified,
extractStyles,
path.resolve(stylesPath, cssFilename),
banner,
minified
),
rollupEsBuild(),
rollupResolve(resolve, (module) =>
resolve.styleExtensions.some((ext) => module.endsWith(ext))
),
{
generateBundle() {
process.stdout.write = () => {
process.stdout.write = ogWrite;
};
},
writeBundle() {
process.stdout.write = ogWrite;
},
},
],
});
return [pipeline(`${file}.css`), pipeline(`${file}.min.css`, true)];
};
+52
View File
@@ -0,0 +1,52 @@
const fs = require('fs');
const { basename } = require('path');
const path = require('path');
const rollupDts = require('rollup-plugin-dts');
const { rollupTs } = require('./pipeline.common.plugins');
module.exports = (resolve, options) => {
const { rollup, paths } = options;
const { output: rollupOutput, input } = rollup;
const { file } = rollupOutput;
const { types: typesPath } = paths;
const dtsOutput = path.resolve(typesPath, `${file}.d.ts`);
return [
{
input,
onwarn: (warning, warn) => {
if (warning.code !== 'EMPTY_BUNDLE') {
warn(warning);
}
},
output: {
file: path.resolve(typesPath, `${file}`),
},
plugins: [rollupTs(input, true)],
},
{
input: path.join(typesPath, `${basename(input).replace('.ts', '.d.ts')}`),
output: {
file: dtsOutput,
},
plugins: [
rollupDts.default({
respectExternal: true,
compilerOptions: {
baseUrl: typesPath,
},
}),
{
writeBundle() {
const filesAndDirs = fs.readdirSync(typesPath);
filesAndDirs.forEach((fileOrDir) => {
if (basename(fileOrDir) !== basename(dtsOutput)) {
fs.rmSync(path.join(typesPath, fileOrDir), { recursive: true });
}
});
},
},
],
},
];
};
@@ -0,0 +1,111 @@
/* eslint-disable no-console */
/* eslint-disable import/no-dynamic-require */
const fs = require('fs');
const path = require('path');
const rollupPluginStyles = require('rollup-plugin-styles');
const rollupPluginServe = require('rollup-plugin-serve');
// const rollupPluginLivereload = require('rollup-plugin-livereload');
const rollupPluginHtml = require('./rollup.pluginHtml');
const createRollupConfig = require('../createRollupConfig');
// const rollupAdditionalWatchFiles = require('./rollup.pluginAdditionalWatchFiles');
const portRange = {
min: 20000,
max: 60000,
};
const meta = {
dist: './.build',
html: './index.html',
input: './index.browser',
};
module.exports = (testDir, mode = 'dev', onListening = null) => {
const name = path.basename(testDir);
const htmlFilePath = path.resolve(testDir, meta.html);
const dist = path.resolve(testDir, meta.dist);
const htmlName = `${name}.html`;
const { min, max } = portRange;
const port = Math.floor(Math.random() * (max - min + 1) + min);
return createRollupConfig({
project: name,
mode,
banner: `${testDir}`,
// if the import would be 'overlayscrollbars' and the package name is also 'overlayscrollbars' esbuild needs an alias to resolve it correctly
alias: (workspaceRoot, workspaces, resolvePath) =>
workspaces.reduce((obj, resolvedPath) => {
const absolutePath = path.resolve(workspaceRoot, resolvedPath);
try {
// eslint-disable-next-line import/no-dynamic-require, global-require
const projTsConfig = require(`${path.resolve(
workspaceRoot,
resolvedPath
)}/tsconfig.json`);
// eslint-disable-next-line import/no-dynamic-require, global-require
const projPackageJson = require(`${path.resolve(
workspaceRoot,
resolvedPath
)}/package.json`);
const { name: projectName } = projPackageJson;
const { compilerOptions } = projTsConfig;
const { baseUrl } = compilerOptions;
obj[projectName] = resolvePath(absolutePath, path.join(baseUrl, projectName), true);
} catch {}
return obj;
}, {}),
paths: {
dist,
},
versions: [
mode === 'dev'
? {
format: 'esm',
generatedCode: 'es2015',
minifiedVersion: false,
}
: {
format: 'iife',
generatedCode: 'es5',
minifiedVersion: false,
},
],
extractStyle: false,
rollup: {
input: path.resolve(testDir, meta.input),
context: 'this',
moduleContext: () => 'this',
output: {
sourcemap: true,
},
plugins: [
rollupPluginStyles(),
rollupPluginHtml(`Playwright: ${name}`, htmlName, () =>
fs.existsSync(htmlFilePath) ? fs.readFileSync(htmlFilePath, 'utf8') : null
),
...(onListening
? [
// rollupAdditionalWatchFiles([htmlFilePath]),
rollupPluginServe({
contentBase: dist,
historyApiFallback: `/${htmlName}`,
host: '127.0.0.1',
port,
onListening,
}),
/*
rollupPluginLivereload({
watch: dist,
port: port - 1,
verbose: false,
}),
*/
]
: []),
],
},
});
};
@@ -0,0 +1,13 @@
const fs = require('fs');
module.exports = (files) => ({
buildStart() {
if (files) {
files.forEach((file) => {
if (fs.existsSync(file)) {
this.addWatchFile(file);
}
});
}
},
});
@@ -0,0 +1,102 @@
const rollupPluginHtml = require('@rollup/plugin-html');
const makeHtmlAttributes = (attributes) => {
if (!attributes) {
return '';
}
const keys = Object.keys(attributes);
// eslint-disable-next-line no-param-reassign
// eslint-disable-next-line no-return-assign
return keys.reduce((result, key) => (result += ` ${key}="${attributes[key]}"`), '');
};
const genHtmlTemplateFunc = (contentOrContentFn) => ({
attributes,
files,
meta,
publicPath,
title,
}) => {
const scripts = (files.js || [])
.map(
({ fileName }) =>
`<script src="${publicPath}${fileName}"${makeHtmlAttributes(attributes.script)}></script>`
)
.join('\n');
const links = (files.css || [])
.map(
({ fileName }) =>
`<link href="${publicPath}${fileName}" rel="stylesheet"${makeHtmlAttributes(
attributes.link
)}>`
)
.join('\n');
const metas = meta.map((input) => `<meta${makeHtmlAttributes(input)}>`).join('\n');
return `<!doctype html>
<html${makeHtmlAttributes(attributes.html)}>
<head>
${metas}
<title>${title}</title>
<style>
html,
body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
}
body {
padding: 10px;
}
*::before,
*::after {
box-sizing: border-box;
}
* {
box-sizing: inherit;
}
#testResult {
display: none;
position: fixed;
top: 0;
right: 0;
padding: 5px;
background: white;
}
#testResult.passed {
display: block;
background: lime;
}
#testResult.passed::before {
content: 'passed';
}
#testResult.failed {
display: block;
background: red;
}
#testResult.failed::before {
content: 'failed';
}
</style>
${links}
</head>
<body>
${(typeof contentOrContentFn === 'function' ? contentOrContentFn() : contentOrContentFn) || ''}
${scripts}
<div id="testResult"></div>
</body>
</html>`;
};
module.exports = (title, fileName, getHtmlContent) =>
rollupPluginHtml({
title,
fileName,
template: genHtmlTemplateFunc(getHtmlContent),
meta: [{ charset: 'utf-8' }, { 'http-equiv': 'X-UA-Compatible', content: 'IE=edge' }],
});
+6
View File
@@ -0,0 +1,6 @@
{
"private": true,
"name": "@~local/tsconfig",
"main": "./tsconfig.json",
"version": "0.0.0"
}
+16
View File
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"strict": true,
"allowJs": false,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"target": "ESNext",
"sourceMap": true,
"jsx": "react",
"declaration": true,
"suppressImplicitAnyIndexErrors": true,
"module": "ESNext",
"moduleResolution": "node",
"removeComments": false
}
}
+18606 -6160
View File
File diff suppressed because it is too large Load Diff
+74 -40
View File
@@ -1,45 +1,79 @@
{
"name": "overlayscrollbars",
"version": "1.13.3",
"description": "A javascript scrollbar plugin which hides native scrollbars, provides custom styleable overlay scrollbars and keeps the native functionality and feeling.",
"keywords": [
"overlayscrollbars",
"custom",
"scrollbar",
"scrollbars",
"scroll",
"frontend"
"private": true,
"workspaces": [
"packages/*"
],
"files": [
"js/jquery.overlayScrollbars.js",
"js/jquery.overlayScrollbars.min.js",
"js/OverlayScrollbars.js",
"js/OverlayScrollbars.min.js",
"css/OverlayScrollbars.css",
"css/OverlayScrollbars.min.css",
"README.md",
"LICENSE"
],
"homepage": "https://kingsora.github.io/OverlayScrollbars",
"repository": {
"type": "git",
"url": "https://github.com/KingSora/OverlayScrollbars"
},
"bugs": {
"url": "https://github.com/KingSora/OverlayScrollbars/issues"
},
"main": "js/OverlayScrollbars.js",
"scripts": {
"build": "node build.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "KingSora | Rene Haas",
"license": "MIT",
"devDependencies": {
"chalk": "^2.4.2",
"csso": "^3.5.1",
"gulp": "^4.0.2",
"shelljs": "^0.8.3",
"uglify-js": "^3.8.1"
"@~local/browser-testing": "file:./local/browser-testing",
"@~local/config": "file:./local/config",
"@~local/full-coverage": "file:./local/full-coverage",
"@~local/playwright-tooling": "file:./local/playwright-tooling",
"@~local/rollup": "file:./local/rollup",
"@~local/tsconfig": "file:./local/tsconfig",
"@babel/core": "^7.18.2",
"@babel/plugin-transform-runtime": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@babel/preset-typescript": "^7.17.12",
"@babel/runtime": "^7.18.2",
"@playwright/test": "^1.22.2",
"@rollup/plugin-alias": "^3.1.9",
"@rollup/plugin-babel": "^5.3.1",
"@rollup/plugin-commonjs": "^22.0.2",
"@rollup/plugin-html": "^0.2.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@testing-library/dom": "^7.26.3",
"@types/jest": "^28.1.6",
"@typescript-eslint/eslint-plugin": "^5.31.0",
"@typescript-eslint/parser": "^5.31.0",
"autoprefixer": "^10.4.7",
"babel-jest": "^28.1.3",
"cssnano": "^5.1.12",
"esbuild": "^0.14.42",
"eslint": "^8.20.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.8.0",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.30.0",
"eslint-plugin-react-hooks": "^4.6.0",
"glob": "^7.1.6",
"jest": "^28.1.3",
"jest-environment-jsdom": "^28.1.3",
"nyc": "^15.1.0",
"playwright": "^1.22.2",
"playwright-chromium": "^1.22.2",
"playwright-core": "^1.22.2",
"playwright-firefox": "^1.22.2",
"playwright-webkit": "^1.22.2",
"postcss": "^8.4.14",
"prettier": "^2.6.2",
"rollup": "^2.75.5",
"rollup-plugin-dts": "^4.2.2",
"rollup-plugin-esbuild": "^4.9.1",
"rollup-plugin-ignore-import": "^1.3.2",
"rollup-plugin-license": "^2.8.1",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-scss": "^3.0.0",
"rollup-plugin-serve": "^1.1.0",
"rollup-plugin-styles": "^4.0.0",
"rollup-plugin-summary": "^1.4.3",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.32.1",
"sass": "^1.54.0",
"should": "^13.2.3",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"v8-to-istanbul": "^9.0.1",
"webpack": "^5.74.0"
},
"scripts": {
"jest": "npm run jest --workspace=overlayscrollbars",
"playwright": "npm run playwright --workspace=overlayscrollbars",
"playwright:dev": "npm run test:playwright:dev --workspace=overlayscrollbars",
"build": "npm run build --workspace=overlayscrollbars",
"lint": "npx eslint --fix ."
}
}
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Rene Haas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-136
View File
@@ -1,136 +0,0 @@
<p align="center">
<a href="https://angular.io/"><img src="https://kingsora.github.io/OverlayScrollbars/frameworks/angular/logo.svg" width="200" height="133" alt="Angular"></a>
<a href="https://kingsora.github.io/OverlayScrollbars/"><img src="https://kingsora.github.io/OverlayScrollbars/design/logo.svg" width="200" height="133" alt="OverlayScrollbars"></a>
</p>
<h6 align="center">
<a href="https://github.com/angular/angular"><img src="https://img.shields.io/badge/Angular-%3E=7.0.0-DD0031?style=flat-square&logo=Angular" alt="Angular"></a>
<a href="https://github.com/KingSora/OverlayScrollbars"><img src="https://img.shields.io/badge/OverlayScrollbars-%5E1.10.0-36befd?style=flat-square" alt="OverlayScrollbars"></a>
<a href="https://www.npmjs.com/package/overlayscrollbars-ngx"><img src="https://img.shields.io/npm/dt/overlayscrollbars-ngx.svg?style=flat-square" alt="Downloads"></a>
<a href="https://github.com/KingSora/OverlayScrollbars/blob/master/packages/overlayscrollbars-ngx/LICENSE"><img src="https://img.shields.io/github/license/kingsora/overlayscrollbars.svg?style=flat-square" alt="License"></a>
</h6>
<h3 align="center">
<a href="https://kingsora.github.io/OverlayScrollbars/frameworks/angular/">Example</a>
&nbsp;&nbsp;&bull;&nbsp;&nbsp;
<a href="https://kingsora.github.io/OverlayScrollbars/#!documentation">Documentation</a>
&nbsp;&nbsp;&bull;&nbsp;&nbsp;
<a href="https://kingsora.github.io/OverlayScrollbars/#!faq">FAQ</a>
</h3>
<h5 align="center">
The official OverlayScrollbars wrapper for Angular.
</h5>
## Installation
```sh
npm install overlayscrollbars-ngx
```
## Peer Dependencies
OverlayScrollbars for Angular has the following **peer dependencies**:
- The vanilla JavaScript library: [overlayscrollbars](https://www.npmjs.com/package/overlayscrollbars)
```
npm install overlayscrollbars
```
- The Angular framework: [@angular/core](https://www.npmjs.com/package/@angular/core)
```
npm install @angular/core
```
## TypeScript
- In case you are using TypeScript, you have to install the [OverlayScrollbars typings](https://www.npmjs.com/package/@types/overlayscrollbars):
```
npm install @types/overlayscrollbars
```
Since this wrapper is written in TypeScript it comes with its generated typings.<br>
Check out the [recommended](https://github.com/KingSora/OverlayScrollbars#typescript) **tsconfig.json** options.
## Usage
#### CSS
You have to import the `OverlayScrollbars.css` by yourself.<br>
The component **doesn't** do it for you as the styles are **global styles**!<br>
There are different ways to achieve this, in Angular the most simple way for me was to add [this line](https://github.com/KingSora/OverlayScrollbars/blob/master/packages/overlayscrollbars-ngx/example/src/styles.css#L1) in the `styles` file:
```css
@import "~overlayscrollbars/css/OverlayScrollbars.css";
```
#### Import
First you need to import the module into your modules file:
```ts
import { NgModule } from "@angular/core";
import { OverlayscrollbarsModule } from "overlayscrollbars-ngx";
@NgModule({
imports: [OverlayscrollbarsModule],
})
export class AppModule {}
```
After that you can import the component into your file(s):
```ts
import { OverlayScrollbarsComponent } from "overlayscrollbars-ngx";
```
#### Template
After the import you can use it in templates like:
```html
<overlay-scrollbars> example content </overlay-scrollbars>
```
#### Properties
Two properties are accepted: `options` and `extensions`.
- The `options` property accepts a `object` and can be changed at any point in time, and the plugin will adapt.
- The `extensions` property accepts a `string`, `string array` or `object` and is only taken into account if the component gets mounted.
```html
<overlay-scrollbars
[options]="{ scrollbars: { autoHide: 'scroll' } }"
[extensions]="['extensionA', 'extensionB']"
>
</overlay-scrollbars>
```
You can read more about the `options` object [here](https://kingsora.github.io/OverlayScrollbars/#!documentation/options), `extensions` are documented [here](https://kingsora.github.io/OverlayScrollbars/#!documentation/extensions-basics) and [here](https://kingsora.github.io/OverlayScrollbars/#!documentation/initialization).
#### Instance
If you get the component reference, it provides two methods: `osInstance()` and `osTarget()`.
- The `osInstance()` method returns the OverlayScrollbars `instance` of the component, or `null` if the instance isn't initialized yet or already destroyed.
- The `osTarget()` method returns the native `html` element to which the plugin was initialized, or `null` if the the component isn't mounted yet or already unmounted.
## Example App
In case you need a example app for reference, you can use the example app in this repo(`example folder`):
- [Live example](https://kingsora.github.io/OverlayScrollbars/frameworks/angular/)
- [Source code](https://github.com/KingSora/OverlayScrollbars/tree/master/packages/overlayscrollbars-ngx/example)
If you wanna build the example app, run these commands:
```sh
npm run setup
npm run build
npm run example
```
## License
MIT
-210
View File
@@ -1,210 +0,0 @@
const packageName = 'overlayscrollbars-ngx';
const rollupUmdName = 'OverlayScrollbarsNgx';
const filesInfo = {
fileName: packageName,
srcFolder: './src',
distFolder: './dist',
typingsFolder: './dist/types',
exampleFolder: './example',
ngcFolder: './ngc',
tsconfigJsonPath: './tsconfig.json',
packageJsonPath: './package.json',
cache: [
'./ngc'
]
}
const packagePaths = {
main: `${filesInfo.distFolder}/${filesInfo.fileName}.js`,
module: `${filesInfo.distFolder}/${filesInfo.fileName}.esm.js`,
typings: `${filesInfo.typingsFolder}/index.d.ts`
}
const rollupUmdGlobals = {
'@angular/core': 'ng.core',
'rxjs': 'rxjs',
'overlayscrollbars': 'OverlayScrollbars'
};
const packageJson = require(filesInfo.packageJsonPath);
const tsconfigJson = require(filesInfo.tsconfigJsonPath);
const path = require('path');
const sh = require('shelljs');
const chalk = require('chalk');
const gulp = require('gulp');
const rollup = require('rollup');
const rollupCommonJs = require('rollup-plugin-commonjs');
const rollupTypeScript = require('rollup-plugin-typescript2');
const rollupResolve = require('rollup-plugin-node-resolve');
const rollupInputConfig = {
input: `${filesInfo.ngcFolder}/${filesInfo.srcFolder}/index.js`,
external: [...Object.keys(packageJson.devDependencies), ...Object.keys(packageJson.peerDependencies)],
plugins: [
rollupResolve(),
rollupCommonJs()
]
};
const rollupOutputConfig = {
exports: 'named',
sourcemap: true
};
sh.echo(chalk.cyanBright('Start building:'));
gulp.task('prepare', function (done) {
sh.echo(`> Removing ${filesInfo.distFolder}`);
sh.rm('-rf', filesInfo.distFolder);
done();
});
gulp.task('ngc', function (done) {
sh.echo('> Compile with "ngc"');
// aot compile .metadata.json: https://github.com/ng-packagr/ng-packagr/blob/master/docs/DESIGN.md#build-artefacts
/*
* make sure metadata.json origins paths are correct, to do that
* create a aotEntry dummy file which for that purpose
*/
let aotEntryFile = `${filesInfo.typingsFolder}/index.ts`;
let srcEntryFile = `${filesInfo.srcFolder}/index.ts`;
let aotImportPath = path.relative(path.dirname(path.resolve(aotEntryFile)), path.dirname(path.resolve(srcEntryFile)));
sh.mkdir('-p', path.dirname(aotEntryFile));
sh.ShellString(`export * from '${aotImportPath.split(path.sep).join('/')}/index'`).to(aotEntryFile);
let newTsconfigJson = {
...tsconfigJson,
// https://github.com/angular/angular/blob/master/packages/compiler-cli/src/transformers/api.ts#L40
angularCompilerOptions: {
...tsconfigJson.angularCompilerOptions,
skipMetadataEmit: false,
strictMetadataEmit: true,
fullTemplateTypeCheck: true,
flatModuleOutFile: `${packageName}.js`,
flatModuleId: packageName
},
compilerOptions: {
...tsconfigJson.compilerOptions,
declarationDir: filesInfo.ngcFolder,
outDir: filesInfo.ngcFolder,
},
files: [aotEntryFile],
include: undefined,
exclude: [filesInfo.distFolder, filesInfo.exampleFolder, 'node_modules']
};
sh.ShellString(JSON.stringify(newTsconfigJson, null, 4)).to(filesInfo.tsconfigJsonPath);
sh.exec('npx ngc -p tsconfig.json');
// delete the aotEntry dummy file
sh.rm('-f', aotEntryFile);
// rename and move metadata.json to correct folder
let aotMetadataJsonPath = path.normalize(`${filesInfo.ngcFolder}/${filesInfo.typingsFolder}/${packageName}.metadata.json`);
let aotMetadataJsonPathNew = path.normalize(`${filesInfo.typingsFolder}/index.metadata.json`);
if (sh.test('-f', aotMetadataJsonPath)) {
sh.echo(chalk.yellowBright(' [metadata.json]: ') + chalk.greenBright(`${aotMetadataJsonPath}${aotMetadataJsonPathNew}`));
sh.mv(aotMetadataJsonPath, aotMetadataJsonPathNew);
}
else {
sh.echo(chalk.redBright(`Couldn't find metadata.json: "${aotMetadataJsonPath}"`));
return;
}
done();
});
gulp.task('tsconfigJson', function (done) {
sh.echo(`> Prepare ${filesInfo.tsconfigJsonPath}`);
let newTsconfigJson = {
...tsconfigJson,
compilerOptions: {
...tsconfigJson.compilerOptions,
declarationDir: filesInfo.typingsFolder,
outDir: filesInfo.distFolder,
},
files: undefined,
include: [filesInfo.srcFolder],
exclude: [filesInfo.distFolder, filesInfo.exampleFolder, 'node_modules']
};
sh.ShellString(JSON.stringify(newTsconfigJson, null, 4)).to(filesInfo.tsconfigJsonPath);
done();
});
gulp.task('packageJson', function (done) {
sh.echo(`> Prepare ${filesInfo.packageJsonPath}`);
let newPackageJson = {
...packageJson,
...packagePaths,
name: packageName,
files: [
path.normalize(filesInfo.srcFolder),
path.normalize(filesInfo.distFolder)
]
};
sh.ShellString(JSON.stringify(newPackageJson, null, 4)).to(filesInfo.packageJsonPath);
done();
});
gulp.task('rollup', function (done) {
sh.echo('> Rollup:');
(async function () {
let rollupTsconfig = {
useTsconfigDeclarationDir: true,
objectHashIgnoreUnknownHack: true,
clean: true,
tsconfig: filesInfo.tsconfigJsonPath,
tsconfigOverride: {
compilerOptions: {
target: 'es5'
}
}
};
rollupTsconfig.tsconfigOverride.compilerOptions.target = 'es5';
let es5umdBundle = await rollup.rollup({
...rollupInputConfig,
plugins: [rollupTypeScript({ ...rollupTsconfig })].concat(rollupInputConfig.plugins)
});
await es5umdBundle.write({
name: rollupUmdName,
globals: rollupUmdGlobals,
file: packagePaths.main,
format: 'umd',
...rollupOutputConfig
});
sh.echo(chalk.yellowBright(` [${rollupTsconfig.tsconfigOverride.compilerOptions.target} & umd]: `) + chalk.greenBright(`${rollupInputConfig.input}${packagePaths.main}`));
rollupTsconfig.tsconfigOverride.compilerOptions.target = 'es6';
let es6esmBundle = await rollup.rollup({
...rollupInputConfig,
plugins: [rollupTypeScript({ ...rollupTsconfig })].concat(rollupInputConfig.plugins)
});
await es6esmBundle.write({
file: packagePaths.module,
format: 'esm',
...rollupOutputConfig
});
sh.echo(chalk.yellowBright(` [${rollupTsconfig.tsconfigOverride.compilerOptions.target} & esm]: `) + chalk.greenBright(`${rollupInputConfig.input}${packagePaths.module}`));
filesInfo.cache.forEach(function (cacheFolder) {
if (sh.test('-d', cacheFolder)) {
sh.rm('-rf', cacheFolder);
}
});
sh.echo(chalk.greenBright('Building finished!'));
done();
})();
});
gulp.task('example', function (done) {
sh.echo();
sh.echo(chalk.cyanBright(`Copy to example → ${filesInfo.exampleFolder}/node_modules/${filesInfo.fileName}`));
sh.mkdir('-p', `${filesInfo.exampleFolder}/node_modules/${filesInfo.fileName}`);
sh.cp('-Rf', [filesInfo.srcFolder, filesInfo.distFolder, filesInfo.packageJsonPath], `${filesInfo.exampleFolder}/node_modules/${filesInfo.fileName}`);
sh.echo(chalk.greenBright('Copying finished!'));
done();
});
gulp.series('prepare', 'ngc', 'tsconfigJson', 'packageJson', 'rollup', 'example')();
@@ -1,67 +0,0 @@
import { Component, ElementRef, NgZone, Input, NgModule } from '@angular/core';
import OverlayScrollbars from 'overlayscrollbars';
var OverlayScrollbarsComponent = (function () {
function OverlayScrollbarsComponent(_osTargetRef, ngZone) {
this.ngZone = ngZone;
this._osInstance = null;
this._osTargetRef = _osTargetRef;
}
OverlayScrollbarsComponent.prototype.osInstance = function () {
return this._osInstance;
};
OverlayScrollbarsComponent.prototype.osTarget = function () {
return this._osTargetRef.nativeElement || null;
};
OverlayScrollbarsComponent.prototype.ngAfterViewInit = function () {
var _this = this;
this.ngZone.runOutsideAngular((function () {
_this._osInstance = OverlayScrollbars(_this.osTarget(), _this._options || {}, _this._extensions);
}));
};
OverlayScrollbarsComponent.prototype.ngOnDestroy = function () {
if (OverlayScrollbars.valid(this._osInstance)) {
this._osInstance.destroy();
this._osInstance = null;
}
};
OverlayScrollbarsComponent.prototype.ngOnChanges = function (changes) {
var optionsChange = changes._options;
if (optionsChange && OverlayScrollbars.valid(this._osInstance)) {
this._osInstance.options(optionsChange.currentValue);
}
};
OverlayScrollbarsComponent.decorators = [
{ type: Component, args: [{
selector: 'overlay-scrollbars',
host: { 'class': 'os-host' },
template: "\n <div class=\"os-resize-observer-host\"></div>\n <div class=\"os-padding\">\n <div class=\"os-viewport\">\n <div class=\"os-content\">\n <ng-content></ng-content>\n </div>\n </div>\n </div>\n <div class=\"os-scrollbar os-scrollbar-horizontal \">\n <div class=\"os-scrollbar-track\">\n <div class=\"os-scrollbar-handle\"></div>\n </div>\n </div>\n <div class=\"os-scrollbar os-scrollbar-vertical\">\n <div class=\"os-scrollbar-track\">\n <div class=\"os-scrollbar-handle\"></div>\n </div>\n </div>\n <div class=\"os-scrollbar-corner\"></div>\n ",
styles: [':host { display: block; }']
},] },
];
OverlayScrollbarsComponent.ctorParameters = function () { return [
{ type: ElementRef },
{ type: NgZone }
]; };
OverlayScrollbarsComponent.propDecorators = {
_options: [{ type: Input, args: ['options',] }],
_extensions: [{ type: Input, args: ['extensions',] }]
};
return OverlayScrollbarsComponent;
}());
var OverlayscrollbarsModule = (function () {
function OverlayscrollbarsModule() {
}
OverlayscrollbarsModule.decorators = [
{ type: NgModule, args: [{
imports: [],
declarations: [OverlayScrollbarsComponent],
exports: [OverlayScrollbarsComponent]
},] },
];
return OverlayscrollbarsModule;
}());
export { OverlayScrollbarsComponent, OverlayscrollbarsModule };
//# sourceMappingURL=overlayscrollbars-ngx.esm.js.map
File diff suppressed because one or more lines are too long
@@ -1,77 +0,0 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('overlayscrollbars')) :
typeof define === 'function' && define.amd ? define(['exports', '@angular/core', 'overlayscrollbars'], factory) :
(global = global || self, factory(global.OverlayScrollbarsNgx = {}, global.ng.core, global.OverlayScrollbars));
}(this, (function (exports, core, OverlayScrollbars) { 'use strict';
OverlayScrollbars = OverlayScrollbars && Object.prototype.hasOwnProperty.call(OverlayScrollbars, 'default') ? OverlayScrollbars['default'] : OverlayScrollbars;
var OverlayScrollbarsComponent = (function () {
function OverlayScrollbarsComponent(_osTargetRef, ngZone) {
this.ngZone = ngZone;
this._osInstance = null;
this._osTargetRef = _osTargetRef;
}
OverlayScrollbarsComponent.prototype.osInstance = function () {
return this._osInstance;
};
OverlayScrollbarsComponent.prototype.osTarget = function () {
return this._osTargetRef.nativeElement || null;
};
OverlayScrollbarsComponent.prototype.ngAfterViewInit = function () {
var _this = this;
this.ngZone.runOutsideAngular((function () {
_this._osInstance = OverlayScrollbars(_this.osTarget(), _this._options || {}, _this._extensions);
}));
};
OverlayScrollbarsComponent.prototype.ngOnDestroy = function () {
if (OverlayScrollbars.valid(this._osInstance)) {
this._osInstance.destroy();
this._osInstance = null;
}
};
OverlayScrollbarsComponent.prototype.ngOnChanges = function (changes) {
var optionsChange = changes._options;
if (optionsChange && OverlayScrollbars.valid(this._osInstance)) {
this._osInstance.options(optionsChange.currentValue);
}
};
OverlayScrollbarsComponent.decorators = [
{ type: core.Component, args: [{
selector: 'overlay-scrollbars',
host: { 'class': 'os-host' },
template: "\n <div class=\"os-resize-observer-host\"></div>\n <div class=\"os-padding\">\n <div class=\"os-viewport\">\n <div class=\"os-content\">\n <ng-content></ng-content>\n </div>\n </div>\n </div>\n <div class=\"os-scrollbar os-scrollbar-horizontal \">\n <div class=\"os-scrollbar-track\">\n <div class=\"os-scrollbar-handle\"></div>\n </div>\n </div>\n <div class=\"os-scrollbar os-scrollbar-vertical\">\n <div class=\"os-scrollbar-track\">\n <div class=\"os-scrollbar-handle\"></div>\n </div>\n </div>\n <div class=\"os-scrollbar-corner\"></div>\n ",
styles: [':host { display: block; }']
},] },
];
OverlayScrollbarsComponent.ctorParameters = function () { return [
{ type: core.ElementRef },
{ type: core.NgZone }
]; };
OverlayScrollbarsComponent.propDecorators = {
_options: [{ type: core.Input, args: ['options',] }],
_extensions: [{ type: core.Input, args: ['extensions',] }]
};
return OverlayScrollbarsComponent;
}());
var OverlayscrollbarsModule = (function () {
function OverlayscrollbarsModule() {
}
OverlayscrollbarsModule.decorators = [
{ type: core.NgModule, args: [{
imports: [],
declarations: [OverlayScrollbarsComponent],
exports: [OverlayScrollbarsComponent]
},] },
];
return OverlayscrollbarsModule;
}());
exports.OverlayScrollbarsComponent = OverlayScrollbarsComponent;
exports.OverlayscrollbarsModule = OverlayscrollbarsModule;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=overlayscrollbars-ngx.js.map
File diff suppressed because one or more lines are too long
-2
View File
@@ -1,2 +0,0 @@
export * from './overlayscrollbars.component';
export * from './overlayscrollbars.module';
@@ -1 +0,0 @@
{"__symbolic":"module","version":4,"metadata":{"OverlayScrollbarsComponent":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Component","line":3,"character":1},"arguments":[{"selector":"overlay-scrollbars","host":{"class":"os-host","$quoted$":["class"]},"template":"\n <div class=\"os-resize-observer-host\"></div>\n <div class=\"os-padding\">\n <div class=\"os-viewport\">\n <div class=\"os-content\">\n <ng-content></ng-content>\n </div>\n </div>\n </div>\n <div class=\"os-scrollbar os-scrollbar-horizontal \">\n <div class=\"os-scrollbar-track\">\n <div class=\"os-scrollbar-handle\"></div>\n </div>\n </div>\n <div class=\"os-scrollbar os-scrollbar-vertical\">\n <div class=\"os-scrollbar-track\">\n <div class=\"os-scrollbar-handle\"></div>\n </div>\n </div>\n <div class=\"os-scrollbar-corner\"></div>\n ","styles":[":host { display: block; }"]}]}],"members":{"_options":[{"__symbolic":"property","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Input","line":30,"character":5},"arguments":["options"]}]}],"_extensions":[{"__symbolic":"property","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Input","line":31,"character":5},"arguments":["extensions"]}]}],"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"reference","module":"@angular/core","name":"ElementRef","line":35,"character":30},{"__symbolic":"reference","module":"@angular/core","name":"NgZone","line":35,"character":58}]}],"osInstance":[{"__symbolic":"method"}],"osTarget":[{"__symbolic":"method"}],"ngAfterViewInit":[{"__symbolic":"method"}],"ngOnDestroy":[{"__symbolic":"method"}],"ngOnChanges":[{"__symbolic":"method"}]}},"OverlayscrollbarsModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule","line":3,"character":1},"arguments":[{"imports":[],"declarations":[{"__symbolic":"reference","name":"OverlayScrollbarsComponent"}],"exports":[{"__symbolic":"reference","name":"OverlayScrollbarsComponent"}]}]}],"members":{}}},"origins":{"OverlayScrollbarsComponent":"../../src/overlayscrollbars.component","OverlayscrollbarsModule":"../../src/overlayscrollbars.module"},"importAs":"overlayscrollbars-ngx"}
@@ -1,15 +0,0 @@
import { ElementRef, SimpleChanges, OnDestroy, OnChanges, AfterViewInit, NgZone } from '@angular/core';
import OverlayScrollbars from 'overlayscrollbars';
export declare class OverlayScrollbarsComponent implements OnDestroy, OnChanges, AfterViewInit {
private ngZone;
_options: OverlayScrollbars.Options;
_extensions: OverlayScrollbars.Extensions;
private _osInstance;
private _osTargetRef;
constructor(_osTargetRef: ElementRef, ngZone: NgZone);
osInstance(): OverlayScrollbars | null;
osTarget(): HTMLDivElement | null;
ngAfterViewInit(): void;
ngOnDestroy(): void;
ngOnChanges(changes: SimpleChanges): void;
}
@@ -1,2 +0,0 @@
export declare class OverlayscrollbarsModule {
}
@@ -1,17 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
@@ -1,16 +0,0 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
@@ -1,46 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
@@ -1,27 +0,0 @@
# Example
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.0.4.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
@@ -1,124 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"example": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/example",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "example:build"
},
"configurations": {
"production": {
"browserTarget": "example:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "example:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "example:serve"
},
"configurations": {
"production": {
"devServerTarget": "example:serve:production"
}
}
}
}
}
},
"defaultProject": "example"
}
@@ -1,37 +0,0 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};
@@ -1,23 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', async () => {
await page.navigateTo();
expect(await page.getTitleText()).toEqual('example app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
@@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
async navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl);
}
async getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText();
}
}
@@ -1,13 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"node"
]
}
}
@@ -1,44 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/example'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
File diff suppressed because it is too large Load Diff
@@ -1,47 +0,0 @@
{
"name": "example",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~11.0.4",
"@angular/common": "~11.0.4",
"@angular/compiler": "~11.0.4",
"@angular/core": "~11.0.4",
"@angular/forms": "~11.0.4",
"@angular/platform-browser": "~11.0.4",
"@angular/platform-browser-dynamic": "~11.0.4",
"@angular/router": "~11.0.4",
"overlayscrollbars": "^1.11.0",
"overlayscrollbars-ngx": "^0.1.0",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1100.4",
"@angular/cli": "~11.0.4",
"@angular/compiler-cli": "~11.0.4",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.1.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.0.2"
}
}
@@ -1,313 +0,0 @@
:host {
min-width: 600px;
display: block;
}
.header {
background: #36befd;
background: -moz-linear-gradient(-45deg, #36befd 1%, #6461f6 100%);
background: -webkit-linear-gradient(-45deg, #36befd 1%, #6461f6 100%);
background: linear-gradient(135deg, #36befd 1%, #6461f6 100%);
margin: 0;
color: #fff;
letter-spacing: 0.1pt;
text-shadow: 0px 1px 3px rgba(0, 0, 255, 0.1);
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
height: 76px;
box-shadow: 0 15px 20px -15px rgba(57, 120, 253, 0.15),
0 55px 50px -35px rgba(47, 78, 249, 0.12);
position: relative;
z-index: 1;
}
.header code {
border-radius: 4px;
margin: 2px;
display: block;
padding: 0.5em;
background: #fff;
font-size: 10pt;
margin: 0px auto;
box-shadow: 0px 1px 3px rgba(0, 0, 255, 0.15);
}
.header code .code-keyword {
color: #0059ff;
font-weight: bold;
}
.header code .code-char {
color: #4d4d4c;
}
.header code .code-variable {
color: #3778ad;
}
.header code .code-string {
color: #279737;
font-weight: 400;
}
.content {
min-height: calc(100vh - 76px);
position: relative;
display: flex;
flex-direction: column;
flex-flow: column;
flex-wrap: wrap;
}
.content-section {
position: relative;
padding: 40px 0px;
background: #fff;
z-index: 1;
overflow: hidden;
}
.content-section:last-child {
flex-grow: 1;
}
.content-section:before,
.content-section:after {
content: "";
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
}
.content-section:nth-child(2n - 1) {
background: #f6f8fb;
}
.content-section:nth-child(2n - 1):before,
.content-section:nth-child(2n - 1):after {
background: #fff;
}
.content-section:nth-child(2n + 0) {
background: #fff;
}
.content-section:nth-child(2n + 0):before,
.content-section:nth-child(2n + 0):after {
background: #f6f8fb;
}
.content-section.skew:before,
.content-section.skew:after {
transform: skewY(-7deg);
}
.content-section.skew + .content-section:before,
.content-section.skew + .content-section:after {
transform: skewY(-7deg);
}
.content-section skew + .content-section.skew:before,
.content-section.skew + .content-section.skew:before {
top: 0;
}
.content-section + .content-section.skew:before {
top: -50vw;
}
.content-section.skew + .content-section:after {
bottom: -50vw;
}
.content-section + .content-section:after {
top: 0;
}
.content-section.skew + .content-section.skew:after {
bottom: 0;
}
.content-section.skew:first-child:before {
top: -50vw !important;
}
.content-section.skew:last-child:after {
bottom: -50vw !important;
}
.content-section-content-framework > span {
font-weight: bold;
font-size: 30pt;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
.content-section-content-framework > span:not(:nth-child(2)) {
display: inline-block;
color: transparent;
height: 190px;
width: 190px;
vertical-align: middle;
}
.content-section-title {
margin-bottom: 14px;
}
.content-section-title > h2 {
text-align: center;
font-size: 26pt;
color: #39547a;
margin: 24px 0px;
}
.content-section-title > table {
text-align: left;
margin: 0px auto;
}
.content-section-title > table tr {
margin: 10px;
}
.content-section-title > table td {
line-height: 18pt;
}
.content-section-title > table td:first-child {
text-align: right;
font-weight: bold;
vertical-align: top;
}
.content-section-title > table td:first-child span {
margin: 0px 6px;
}
.content-section-title > table td:last-child span {
text-align: left;
color: #36befd;
font-weight: bold;
}
.content-section-content {
display: table;
margin: 0px auto;
}
.content-section-content .os-host,
.content-section-content .os-host-textarea {
border: 2px solid #36befd;
width: 480px;
max-height: 300px;
margin: 10px auto 20px auto;
border-radius: 6px;
padding: 10px;
line-height: 16pt;
}
.content-section-content > .os-host .os-host,
.content-section-content > .os-host-textarea .os-host-textarea {
border: 2px solid #6461f6;
width: auto;
height: auto;
margin: 10px auto;
}
.content-section-content .bonus-content {
display: inline-block;
white-space: pre;
background: #f0f3f6;
padding: 0px 5px;
margin: 2px;
border-radius: 4px !important;
border: 1px solid #dde3ed;
font-size: 10pt;
font-family: "Lucida Console", Monaco, monospace;
color: #39547a;
}
.content-section-content-buttons {
display: table;
margin: 0px auto;
}
.info-span {
background: #f7f7f7;
padding: 2px 5px;
margin: 2px;
white-space: nowrap;
border-radius: 4px;
border: 1px solid #dedfe0;
font-weight: bold;
font-size: 10pt;
}
a {
display: inline-block;
text-decoration: none;
position: relative;
color: #36befd;
transition: color 0.3s, text-shadow 0.3s;
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.05);
padding: 0px 1px;
font-weight: 600;
outline: none !important;
cursor: pointer;
z-index: 0;
}
a:hover {
color: #fff;
text-shadow: 0px 1px 6px rgba(0, 0, 0, 0.16);
}
a:before {
content: "";
position: absolute;
display: block;
bottom: 0;
left: 0;
height: 0%;
width: 100%;
background: transparent;
z-index: -1;
border-bottom: 1px dotted #36befd;
transition: height 0.3s, border 0.3s, background-color 0.15s;
}
a:hover:before {
height: 100%;
background: #36befd;
border-bottom: 1px solid #36befd;
}
.buttons {
display: table;
margin: 0px auto;
}
button {
font-size: 10pt;
line-height: 28pt;
font-family: sans-serif;
font-weight: bold;
color: #555e6b;
line-height: 40px;
border: 1px solid #d6d6d6;
border-radius: 6px;
cursor: pointer;
transition: color 0.3s, background-color 0.3s, border-color 0.3s,
box-shadow 0.3s;
padding: 0px 14px;
display: block;
float: left;
margin: 5px;
text-align: center;
background: rgba(0, 0, 0, 0.02);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
min-width: 80px;
outline: none !important;
}
button:hover {
color: #fff;
background: #6461f6;
border-color: #6461f6;
box-shadow: 0 4px 8px -1px rgba(170, 170, 170, 0.45);
}
button:active {
box-shadow: inset 0 4px 9px -1px rgba(0, 0, 0, 0.15);
}
::selection {
color: #fff;
background: #6461f6;
text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.28);
}
img {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
}
.os-logo {
background: transparent url("../assets/overlayscrollbars.svg") no-repeat
center center;
background-size: 80%;
}
.framework-logo {
background: transparent url("../assets/angular.svg") no-repeat center center;
background-size: 80%;
}
.custom-class-name-test {
background: rgba(0, 0, 0, 0.03);
}
@@ -1,90 +0,0 @@
<div class="header">
<code>
<span class="code-keyword">import</span>
<span class="code-char">{{ " { " }}</span>
<span class="code-variable">OverlayScrollbarsComponent</span>
<span class="code-char">{{ " } " }}</span>
<span class="code-keyword">from</span>
<span class="code-string">{{ " 'overlayscrollbars-ngx'" }}</span>
<span class="code-char">;</span>
</code>
</div>
<div class="content">
<div class="content-section skew">
<div class="content-section-content content-section-content-framework">
<span class="framework-logo"></span>
<span>+</span>
<span class="os-logo"></span>
</div>
</div>
<div class="content-section">
<div class="content-section-title">
<h2>Component</h2>
<table>
<tbody>
<tr>
<td>
<span>Class:</span>
</td>
<td>
<span>{{ componentClass }}</span>
</td>
</tr>
<tr>
<td>
<span>Description:</span>
</td>
<td>OverlayScrollbars as a {{ framework }}-Component.</td>
</tr>
</tbody>
</table>
</div>
<div class="content-section-content">
<overlay-scrollbars
#osComponentRef1
[options]="osComponentOptions"
[ngStyle]="{ maxHeight: '350px' }"
[ngClass]="[framework, hasCustomClassName ? customClassName : '']"
>
<div class="bonus-content">{{ componentContent }}</div>
{{ loremIpsumShort }}
<overlay-scrollbars
#osComponentRef2
[options]="osComponentOptions"
[ngStyle]="{ maxHeight: '150px' }"
[ngClass]="[hasCustomClassName ? customClassName : '']"
>
<div class="bonus-content">{{ componentContent }}</div>
{{ loremIpsumLong }}
<br />
<br />
{{ loremIpsumShort }}
</overlay-scrollbars>
{{ loremIpsumMedium }}
<br />
<br />
{{ loremIpsumShort }}
<br />
<br />
{{ loremIpsumLong }}
<div *ngFor="let item of loremList; let i = index" [attr.data-key]="i">
<br />
{{ item }}
</div>
</overlay-scrollbars>
<div class="buttons">
<button
(click)="
onBtnScrollRandom($event, [osComponentRef1, osComponentRef2])
"
>
Scroll
</button>
<button (click)="onBtnChangeOptions()">Change Options</button>
<button (click)="onBtnChangeContent()">Change Content</button>
<button (click)="onBtnLog()">Log</button>
</div>
</div>
</div>
</div>
@@ -1,25 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AppComponent],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain(
'example app is running!'
);
});
});
@@ -1,109 +0,0 @@
import { Component, ViewChild, ElementRef, Input } from '@angular/core';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-ngx';
import OverlayScrollbars from 'overlayscrollbars';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
framework: string = 'Angular';
customClassName: string = 'custom-class-name-test';
componentClass: string = 'OverlayScrollbarsComponent';
loremIpsumLong: string =
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';
loremIpsumMedium: string =
'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga.';
loremIpsumShort: string =
'Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio.';
loremList: Array<string> = [];
hasCustomClassName: boolean = false;
componentContent: string = 'Lorem Ipsum';
osComponentOptions: OverlayScrollbars.Options = {
resize: 'both',
paddingAbsolute: true,
scrollbars: {
autoHide: 'never',
},
};
@ViewChild('osComponentRef1', { read: OverlayScrollbarsComponent })
osComponentRef1: OverlayScrollbarsComponent;
@ViewChild('osComponentRef2', { read: OverlayScrollbarsComponent })
osComponentRef2: OverlayScrollbarsComponent;
ngAfterViewInit() {
console.log(`${this.componentClass} (1)`);
console.log(this.osComponentRef1);
console.log(`${this.componentClass} (2)`);
console.log(this.osComponentRef2);
}
onBtnScrollRandom(
event: any,
refArray: ReadonlyArray<OverlayScrollbarsComponent>
) {
if (refArray) {
for (let i = 0; i < refArray.length; i++) {
if (refArray[i]) {
const osInstance = refArray[i].osInstance();
osInstance.scrollStop().scroll(
{
x: Math.floor(Math.random() * osInstance.scroll().max.x + 0),
y: Math.floor(Math.random() * osInstance.scroll().max.y + 0),
},
1000,
'easeOutElastic'
);
}
}
}
}
onBtnChangeOptions() {
this.hasCustomClassName = !this.hasCustomClassName;
this.osComponentOptions = {
resize: this.osComponentOptions.resize === 'both' ? 'none' : 'both',
scrollbars: {
autoHide:
this.osComponentOptions.scrollbars.autoHide === 'never'
? 'scroll'
: 'never',
},
};
}
onBtnChangeContent() {
this.componentContent = this.componentContent + '\r\n' + this.randomIpsum();
this.loremList.push(this.randomIpsum());
}
onBtnLog() {
console.log(`== ${this.componentClass} (1) ==`);
console.log('Ref:');
console.log(this.osComponentRef1);
console.log('Instance:');
console.log(this.osComponentRef1.osInstance());
console.log('Target:');
console.log(this.osComponentRef1.osTarget());
console.log('');
console.log(`== ${this.componentClass} (2) ==`);
console.log('Ref:');
console.log(this.osComponentRef2);
console.log('Instance:');
console.log(this.osComponentRef2.osInstance());
console.log('Target:');
console.log(this.osComponentRef2.osTarget());
}
randomIpsum(): string {
const loremIpsums = [
this.loremIpsumLong,
this.loremIpsumMedium,
this.loremIpsumShort,
];
const random = Math.floor(Math.random() * loremIpsums.length);
return loremIpsums[random];
}
}
@@ -1,12 +0,0 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { OverlayscrollbarsModule } from 'overlayscrollbars-ngx';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, OverlayscrollbarsModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

Some files were not shown because too many files have changed in this diff Show More