feat(All):Initial
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// bundle entry point
|
||||
|
||||
export * from './timeline';
|
||||
export * from './timelineEventsEmitter';
|
||||
export * from './settings/timelineConsts';
|
||||
// @ public timeline models.
|
||||
export * from './models/timelineRanged';
|
||||
export * from './models/timelineModel';
|
||||
export * from './models/timelineRow';
|
||||
export * from './models/timelineKeyframe';
|
||||
|
||||
// @public styles
|
||||
export * from './settings/timelineOptions';
|
||||
export * from './settings/styles/timelineKeyframeStyle';
|
||||
export * from './settings/styles/timelineRowStyle';
|
||||
export * from './settings/styles/timelineStyle';
|
||||
export * from './settings/styles/timelineGroupStyle';
|
||||
|
||||
export * from './utils/timelineStyleUtils';
|
||||
export * from './utils/timelineUtils';
|
||||
export * from './utils/timelineElement';
|
||||
|
||||
// @private helper containers.
|
||||
export * from './utils/timelineSelectable';
|
||||
export * from './utils/timelineCutBoundsRectResults';
|
||||
export * from './utils/timelineSelectionResults';
|
||||
export * from './utils/timelinePoint';
|
||||
export * from './utils/timelineMouseData';
|
||||
export * from './utils/timelineElementDragState';
|
||||
export * from './utils/timelineDraggableData';
|
||||
|
||||
// @private virtual model
|
||||
export * from './viewModels/timelineGroupViewModel';
|
||||
export * from './viewModels/timelineKeyframeViewModel';
|
||||
export * from './viewModels/timelineRowViewModel';
|
||||
export * from './viewModels/timelineViewModel';
|
||||
|
||||
// @public events
|
||||
export * from './utils/events/timelineKeyframeChangedEvent';
|
||||
export * from './utils/events/timelineTimeChangedEvent';
|
||||
export * from './utils/events/timelineSelectedEvent';
|
||||
export * from './utils/events/timelineScrollEvent';
|
||||
export * from './utils/events/timelineClickEvent';
|
||||
export * from './utils/events/timelineDragEvent';
|
||||
|
||||
// @public enums
|
||||
export * from './enums/timelineKeyframeShape';
|
||||
export * from './enums/timelineInteractionMode';
|
||||
export * from './enums/timelineScrollSource';
|
||||
export * from './enums/timelineElementType';
|
||||
export * from './enums/timelineCursorType';
|
||||
export * from './enums/timelineCapShape';
|
||||
export * from './enums/timelineEventSource';
|
||||
export * from './enums/timelineSelectionMode';
|
||||
export * from './enums/timelineEvents';
|
||||
// @private defaults are exposed:
|
||||
export * from './settings/defaults/defaultTimelineStyle';
|
||||
export * from './settings/defaults/defaultTimelineRowStyle';
|
||||
export * from './settings/defaults/defaultTimelineOptions';
|
||||
export * from './settings/defaults/defaultTimelineKeyframeStyle';
|
||||
export * from './settings/defaults/defaultTimelineConsts';
|
||||
export * from './settings/defaults/defaultGroupStyle';
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum TimelineCapShape {
|
||||
None = 'none',
|
||||
Triangle = 'triangle',
|
||||
Rect = 'rect',
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export enum TimelineCursorType {
|
||||
Alias = 'alias',
|
||||
AllScroll = 'all-scroll',
|
||||
Auto = 'auto',
|
||||
Cell = 'cell',
|
||||
ContextMenu = 'context-menu',
|
||||
ColResize = 'col-resize',
|
||||
Copy = 'copy',
|
||||
Crosshair = 'crosshair',
|
||||
Default = 'default',
|
||||
EResize = 'e-resize',
|
||||
EWResize = 'ew-resize',
|
||||
Grab = 'grab',
|
||||
Grabbing = 'grabbing',
|
||||
Help = 'help',
|
||||
Move = 'move',
|
||||
NResize = 'n-resize',
|
||||
NEResize = 'ne-resize',
|
||||
NESWResize = 'nesw-resize',
|
||||
NSResize = 'ns-resize',
|
||||
NWResize = 'nw-resize',
|
||||
NWSEResize = 'nwse-resize',
|
||||
NoDrop = 'no-drop',
|
||||
None = 'none',
|
||||
NotAllowed = 'not-allowed',
|
||||
Pointer = 'pointer',
|
||||
Progress = 'progress',
|
||||
RowResize = 'row-resize',
|
||||
SResize = 's-resize',
|
||||
SEResize = 'se-resize',
|
||||
SWResize = 'sw-resize',
|
||||
Text = 'text',
|
||||
WResize = 'w-resize',
|
||||
Wait = 'wait',
|
||||
ZoomIn = 'zoom-in',
|
||||
ZoomOut = 'zoom-out',
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Internal element type.
|
||||
*/
|
||||
export enum TimelineElementType {
|
||||
/**
|
||||
* Timeline
|
||||
*/
|
||||
Timeline = 'timeline',
|
||||
/**
|
||||
* Keyframes
|
||||
*/
|
||||
Keyframe = 'keyframe',
|
||||
/**
|
||||
* Keyframes connected and presenting one group.
|
||||
*/
|
||||
Group = 'group',
|
||||
/**
|
||||
* Timeline row.
|
||||
*/
|
||||
Row = 'row',
|
||||
None = 'none',
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export enum TimelineEventSource {
|
||||
/**
|
||||
* Changed by user interaction events.
|
||||
*/
|
||||
User = 'user',
|
||||
/**
|
||||
* Changed programmatically.
|
||||
*/
|
||||
Programmatically = 'programmatically',
|
||||
/**
|
||||
* Changed by the set time function.
|
||||
*/
|
||||
SetTimeMethod = 'setTimeMethod',
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Event names of the component.
|
||||
*/
|
||||
export enum TimelineEvents {
|
||||
Selected = 'selected',
|
||||
TimeChanged = 'timechanged',
|
||||
KeyframeChanged = 'keyframeChanged',
|
||||
DragStarted = 'dragStarted',
|
||||
Drag = 'drag',
|
||||
DragFinished = 'dragFinished',
|
||||
Scroll = 'scroll',
|
||||
ScrollFinished = 'scrollFinished',
|
||||
ContextMenu = 'onContextMenu',
|
||||
DoubleClick = 'doubleClick',
|
||||
MouseDown = 'mouseDown',
|
||||
Zoom = 'zoom',
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
export enum TimelineInteractionMode {
|
||||
/**
|
||||
* Keyframe selection tool selecting single or group of keyframes.
|
||||
*/
|
||||
Selection = 'selection',
|
||||
/**
|
||||
* Pan tool with the possibility to select keyframes.
|
||||
*/
|
||||
Pan = 'pan',
|
||||
/**
|
||||
* Allow only pan without any keyframes interaction.
|
||||
* Timeline still can be moved and controlled by option 'timelineDraggable'.
|
||||
*/
|
||||
NonInteractivePan = 'nonInteractivePan',
|
||||
/**
|
||||
* Zoom tool.
|
||||
*/
|
||||
Zoom = 'zoom',
|
||||
|
||||
/**
|
||||
* No iteraction, except moving a timeline.
|
||||
* Timeline still can be moved and controlled by option 'timelineDraggable'.
|
||||
*/
|
||||
None = 'none',
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export enum TimelineKeyframeShape {
|
||||
None = 'none',
|
||||
Rhomb = 'rhomb',
|
||||
Circle = 'circle',
|
||||
Rect = 'rect',
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum TimelineScrollSource {
|
||||
DefaultMode = 'none',
|
||||
ZoomMode = 'zoom',
|
||||
ScrollBySelection = 'scrollBySelection',
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Timeline selection event type.
|
||||
*/
|
||||
export enum TimelineSelectionEventSource {
|
||||
/**
|
||||
* Keyframe selection is performed.
|
||||
*/
|
||||
Keyframes = 'keyframes',
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Timeline selection mode.
|
||||
*/
|
||||
export enum TimelineSelectionMode {
|
||||
/**
|
||||
* Select new items. deselect changed.
|
||||
*/
|
||||
Normal = 'normal',
|
||||
/**
|
||||
* Append current selection.
|
||||
*/
|
||||
Append = 'append',
|
||||
/**
|
||||
* Revert selection of a specified nodes.
|
||||
*/
|
||||
Revert = 'revert',
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { TimelineKeyframeStyle } from '../settings/styles/timelineKeyframeStyle';
|
||||
import { TimelineGroupStyle } from '../settings/styles/timelineGroupStyle';
|
||||
/**
|
||||
* Timeline group view model.
|
||||
*/
|
||||
export interface TimelineGroup {
|
||||
/**
|
||||
* Group style.
|
||||
*/
|
||||
style: TimelineGroupStyle;
|
||||
/**
|
||||
* Child keyframes style.
|
||||
*/
|
||||
keyframesStyle?: TimelineKeyframeStyle;
|
||||
/**
|
||||
* Whether group is draggable.
|
||||
* Considered to be false when really set as false.
|
||||
*/
|
||||
draggable?: boolean;
|
||||
/**
|
||||
* Whether group keyframes are draggable.
|
||||
*/
|
||||
keyframesDraggable?: boolean;
|
||||
/**
|
||||
* Whether group is hidden.
|
||||
*/
|
||||
hidden?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { TimelineKeyframeStyle } from '../settings/styles/timelineKeyframeStyle';
|
||||
import { TimelineSelectable } from '../utils/timelineSelectable';
|
||||
import { TimelineRanged } from './timelineRanged';
|
||||
import { TimelineGroup } from './timelineGroup';
|
||||
|
||||
export interface TimelineKeyframe extends TimelineSelectable, TimelineRanged {
|
||||
/**
|
||||
* Keyframe value.
|
||||
*/
|
||||
val: number;
|
||||
/**
|
||||
* Related keyframe group.
|
||||
* Timeline keyframes groups are rendered as one instance.
|
||||
*/
|
||||
group?: string | TimelineGroup;
|
||||
/**
|
||||
* Keyframe style.
|
||||
*/
|
||||
style?: TimelineKeyframeStyle;
|
||||
/**
|
||||
* Whether keyframe is hidden.
|
||||
*/
|
||||
hidden?: boolean;
|
||||
/**
|
||||
* Whether group is draggable.
|
||||
* Considered to be false when really set as false.
|
||||
*/
|
||||
draggable?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { TimelineRow } from './timelineRow';
|
||||
export interface TimelineModel {
|
||||
rows: TimelineRow[];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface TimelineRanged {
|
||||
/**
|
||||
* min
|
||||
*/
|
||||
min?: number | null;
|
||||
/**
|
||||
* max.
|
||||
*/
|
||||
max?: number | null;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { TimelineKeyframe } from './timelineKeyframe';
|
||||
import { TimelineRowStyle } from '../settings/styles/timelineRowStyle';
|
||||
import { TimelineRanged } from './timelineRanged';
|
||||
|
||||
export interface TimelineRow extends TimelineRanged {
|
||||
style?: TimelineRowStyle;
|
||||
keyframes?: TimelineKeyframe[] | null;
|
||||
hidden?: boolean;
|
||||
/**
|
||||
* Whether group keyframes are draggable.
|
||||
*/
|
||||
keyframesDraggable?: boolean;
|
||||
groupsDraggable?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { TimelineGroupStyle } from '../styles/timelineGroupStyle';
|
||||
export const defaultRowHeight = 24;
|
||||
const margin = 4;
|
||||
export const defaultGroupStyle = {
|
||||
height: 'auto',
|
||||
marginTop: margin,
|
||||
/**
|
||||
* Default group fill color.
|
||||
*/
|
||||
fillColor: '#094771',
|
||||
/**
|
||||
* 组默认文本设置
|
||||
*/
|
||||
text: {
|
||||
label: "",
|
||||
isStroke:false,
|
||||
font: '10px sans-serif',
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
direction:"inherit",
|
||||
fillColor: '#fff'
|
||||
}
|
||||
} as TimelineGroupStyle;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { TimelineConsts } from '../timelineConsts';
|
||||
|
||||
export const defaultTimelineConsts: TimelineConsts = {
|
||||
/**
|
||||
* Private. Auto pan speed.
|
||||
*/
|
||||
autoPanSpeed: 50,
|
||||
/**
|
||||
* Private. scroll speed when mouse drag is used (from 0 to 1)
|
||||
*/
|
||||
scrollByDragSpeed: 0.12,
|
||||
/**
|
||||
* Private. Determine whether item was clicked.
|
||||
*/
|
||||
clickDetectionMs: 120,
|
||||
/**
|
||||
* Private. Timeout to detect double click.
|
||||
*/
|
||||
doubleClickTimeoutMs: 400,
|
||||
/**
|
||||
* Private. Time in ms used to refresh scrollbars when pan is finished.
|
||||
*/
|
||||
scrollFinishedTimeoutMs: 500,
|
||||
/**
|
||||
* Private. Auto pan padding
|
||||
*/
|
||||
autoPanByScrollPadding: 10,
|
||||
/**
|
||||
* Private. Click threshold
|
||||
*/
|
||||
clickThreshold: 3,
|
||||
/**
|
||||
* Private. Private.Click min radius for the elements detection.
|
||||
*/
|
||||
clickDetectionMinRadius: 2,
|
||||
/**
|
||||
* Private. Skip some auto pan/scroll actions if they are executed more rapid than this value.
|
||||
*/
|
||||
autoPanSpeedLimit: 10,
|
||||
/**
|
||||
* Private. Default auto size for the group. It's percents.
|
||||
*/
|
||||
defaultGroupHeight: 0.7,
|
||||
} as TimelineConsts;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { TimelineKeyframeShape } from '../../enums/timelineKeyframeShape';
|
||||
import { TimelineKeyframeStyle } from '../styles/timelineKeyframeStyle';
|
||||
|
||||
export const defaultTimelineKeyframeStyle = {
|
||||
/**
|
||||
* keyframe fill color.
|
||||
*/
|
||||
fillColor: 'DarkOrange',
|
||||
shape: TimelineKeyframeShape.Rhomb,
|
||||
/**
|
||||
* Selected keyframe fill color.
|
||||
*/
|
||||
selectedFillColor: 'red',
|
||||
strokeColor: 'black',
|
||||
selectedStrokeColor: 'black',
|
||||
strokeThickness: 0.2,
|
||||
height: 'auto',
|
||||
width: 'auto',
|
||||
} as TimelineKeyframeStyle;
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
import { TimelineOptions } from '../timelineOptions';
|
||||
import { defaultGroupStyle } from './defaultGroupStyle';
|
||||
import { defaultTimelineKeyframeStyle } from './defaultTimelineKeyframeStyle';
|
||||
import { defaultTimelineRowStyle } from './defaultTimelineRowStyle';
|
||||
import { defaultTimelineStyle } from './defaultTimelineStyle';
|
||||
|
||||
export const defaultTimelineOptions = {
|
||||
/**
|
||||
* Snap all selected keyframes as a bundle during the drag.
|
||||
*/
|
||||
snapAllKeyframesOnMove: false,
|
||||
|
||||
/**
|
||||
* Check whether snapping is enabled.
|
||||
*/
|
||||
snapEnabled: true,
|
||||
|
||||
/**
|
||||
* Timeline style.
|
||||
*/
|
||||
timelineStyle: defaultTimelineStyle,
|
||||
/**
|
||||
* approximate step for the timeline in pixels for 1 second
|
||||
*/
|
||||
stepPx: 120,
|
||||
/**
|
||||
* Number of units that should fit into one stepPx. (1 second by a default)
|
||||
*/
|
||||
stepVal: 1000,
|
||||
stepSmallPx: 30,
|
||||
/**
|
||||
* Snap step in units. from 0 to stepVal
|
||||
*/
|
||||
snapStep: 200,
|
||||
/**
|
||||
* additional left margin in pixels to start the line gauge from.
|
||||
*/
|
||||
leftMargin: 25,
|
||||
headerFillColor: '#101011',
|
||||
fillColor: '#101011',
|
||||
|
||||
labelsColor: '#D5D5D5',
|
||||
/**
|
||||
* Header gauge tick color.
|
||||
*/
|
||||
tickColor: '#D5D5D5',
|
||||
/**
|
||||
* Selection rectangle color.
|
||||
*/
|
||||
selectionColor: 'White',
|
||||
|
||||
/**
|
||||
* Default rows style.
|
||||
* Can be overridden by setting style individually for each row.
|
||||
*/
|
||||
rowsStyle: defaultTimelineRowStyle,
|
||||
|
||||
/**
|
||||
* Style for the all keyframes in a current row.
|
||||
* Individual keyframe can have own style.
|
||||
*/
|
||||
keyframesStyle: defaultTimelineKeyframeStyle,
|
||||
/**
|
||||
* Style of the groups.
|
||||
*/
|
||||
groupsStyle: defaultGroupStyle,
|
||||
/**
|
||||
* Header height in pixels
|
||||
*/
|
||||
headerHeight: 30,
|
||||
font: '11px sans-serif',
|
||||
/**
|
||||
* Default zoom level = 1. where screen pixels are equals to the corresponding stepVal stepPx.
|
||||
*/
|
||||
zoom: 1,
|
||||
/**
|
||||
* Default zoom speed.
|
||||
*/
|
||||
zoomSpeed: 0.1,
|
||||
/**
|
||||
* Max zoom value.
|
||||
*/
|
||||
zoomMin: 0.1,
|
||||
/**
|
||||
* Min zoom value.
|
||||
*/
|
||||
zoomMax: 8,
|
||||
/**
|
||||
* Set this to true in a MAC OS environment: The Meta key will be used instead of the Ctrl key.
|
||||
*/
|
||||
controlKeyIsMetaKey: false,
|
||||
/**
|
||||
* Access the scroll container via this class for e.g. scroll bar styling.
|
||||
*/
|
||||
scrollContainerClass: 'scroll-container',
|
||||
/**
|
||||
* keyframes group is draggable.
|
||||
*/
|
||||
groupsDraggable: true,
|
||||
/**
|
||||
* keyframes are draggable.
|
||||
*/
|
||||
keyframesDraggable: true,
|
||||
/**
|
||||
* Timeline can be dragged or position can be changed by user interaction. Default: true
|
||||
*/
|
||||
timelineDraggable: true,
|
||||
/**
|
||||
* Start drawing timeline from this min point.
|
||||
* Bounds for the keyframe dragging.
|
||||
*/
|
||||
min: 0,
|
||||
/**
|
||||
* Max bounds timeline can navigate to.
|
||||
*/
|
||||
max: Number.MAX_VALUE,
|
||||
} as TimelineOptions;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { TimelineRowStyle } from '../styles/timelineRowStyle';
|
||||
import { defaultGroupStyle, defaultRowHeight } from './defaultGroupStyle';
|
||||
import { defaultTimelineKeyframeStyle } from './defaultTimelineKeyframeStyle';
|
||||
|
||||
export const defaultTimelineRowStyle = {
|
||||
/**
|
||||
* Row height in pixels.
|
||||
*/
|
||||
height: defaultRowHeight,
|
||||
marginBottom: 2,
|
||||
fillColor: '#252526',
|
||||
/**
|
||||
* Style for the all keyframes in a current row.
|
||||
* Individual keyframe can have own style.
|
||||
*/
|
||||
keyframesStyle: defaultTimelineKeyframeStyle,
|
||||
/**
|
||||
* Style of the groups.
|
||||
*/
|
||||
groupsStyle: defaultGroupStyle,
|
||||
} as TimelineRowStyle;
|
||||
@@ -0,0 +1,22 @@
|
||||
import { TimelineCapShape } from '../../enums/timelineCapShape';
|
||||
import { TimelineCursorType } from '../../enums/timelineCursorType';
|
||||
import { TimelineStyle } from '../styles/timelineStyle';
|
||||
|
||||
export const defaultTimelineStyle = {
|
||||
width: 2,
|
||||
marginTop: 15,
|
||||
marginBottom: 0,
|
||||
strokeColor: 'DarkOrange',
|
||||
fillColor: 'DarkOrange',
|
||||
capStyle: {
|
||||
width: 4,
|
||||
height: 10,
|
||||
/**
|
||||
* Draw timeline rectangular cap.
|
||||
*/
|
||||
capType: TimelineCapShape.Rect,
|
||||
strokeColor: 'DarkOrange',
|
||||
fillColor: 'DarkOrange',
|
||||
},
|
||||
cursor: TimelineCursorType.EWResize,
|
||||
} as TimelineStyle;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { TimelineCapShape } from '../../enums/timelineCapShape';
|
||||
|
||||
/**
|
||||
* Timeline active/current value indicator style.
|
||||
*/
|
||||
export interface TimelineCapStyle {
|
||||
/**
|
||||
* Cap style width in pixels.
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* Cap style height in pixels.
|
||||
*/
|
||||
height?: number;
|
||||
/**
|
||||
* Cap stroke color.
|
||||
*/
|
||||
strokeColor?: string;
|
||||
/**
|
||||
* Cap fill color.
|
||||
*/
|
||||
fillColor?: string;
|
||||
/**
|
||||
* Cap type
|
||||
*/
|
||||
capType?: TimelineCapShape;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { TimelineCursorType } from '../../enums/timelineCursorType';
|
||||
import { TimelineKeyframeStyle } from '../styles/timelineKeyframeStyle';
|
||||
|
||||
/**
|
||||
* Timeline group style.
|
||||
*/
|
||||
export interface TimelineGroupStyle {
|
||||
/**
|
||||
* Keyframes style height in pixels.
|
||||
* 'auto' to automatically calculate.
|
||||
*/
|
||||
height?: number | string;
|
||||
/**
|
||||
* Group stroke color.
|
||||
*/
|
||||
strokeColor?: string;
|
||||
/**
|
||||
* Group stroke thickness.
|
||||
*/
|
||||
strokeThickness?: number | null;
|
||||
|
||||
/**
|
||||
* Group border radius. See canvas roundRect official documentation.
|
||||
*/
|
||||
radii?: number | DOMPointInit | Iterable<number | DOMPointInit>;
|
||||
|
||||
/**
|
||||
* Group fill color.
|
||||
*/
|
||||
fillColor?: string;
|
||||
/**
|
||||
* Group mouse over cursor style.
|
||||
*/
|
||||
cursor?: TimelineCursorType;
|
||||
/**
|
||||
* Margin top in px or 'auto' to center element.
|
||||
*/
|
||||
marginTop?: number | string;
|
||||
/**
|
||||
* Style for all the keyframes in the current group.
|
||||
*/
|
||||
keyframesStyle?: TimelineKeyframeStyle;
|
||||
|
||||
/**
|
||||
* 组默认文本设置
|
||||
*/
|
||||
text?: {
|
||||
label?: string,
|
||||
isStroke?: boolean,
|
||||
font?: string,
|
||||
textAlign?: "start" | "end" | "left" | "right" | "center",
|
||||
textBaseline?: "top" | "hanging" | "middle" | "alphabetic" | "ideographic" | "bottom",
|
||||
direction?: "ltr" | "rtl" | "inherit",
|
||||
fillColor?: string,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { TimelineCursorType } from '../../enums/timelineCursorType';
|
||||
import { TimelineKeyframeShape } from '../../enums/timelineKeyframeShape';
|
||||
|
||||
export interface TimelineKeyframeStyle {
|
||||
/**
|
||||
* Timeline cursor style.
|
||||
*/
|
||||
cursor?: TimelineCursorType;
|
||||
/**
|
||||
* Timeline keyframe shape
|
||||
*/
|
||||
shape?: TimelineKeyframeShape;
|
||||
/**
|
||||
* keyframe size, number or text 'auto'
|
||||
*/
|
||||
height?: number | string;
|
||||
/**
|
||||
* keyframe size, number or text 'auto'
|
||||
*/
|
||||
width?: number | string;
|
||||
/**
|
||||
* Keyframe fill color
|
||||
*/
|
||||
fillColor?: string | null;
|
||||
/**
|
||||
* Keyframe selected fill color.
|
||||
*/
|
||||
selectedFillColor?: string | null;
|
||||
/**
|
||||
* Keyframe stroke color.
|
||||
*/
|
||||
strokeColor?: string | null;
|
||||
/**
|
||||
* Keyframe selected stroke color.
|
||||
*/
|
||||
selectedStrokeColor?: string | null;
|
||||
/**
|
||||
* Keyframe stroke Thickness.
|
||||
*/
|
||||
strokeThickness?: number | null;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { TimelineGroupStyle } from './timelineGroupStyle';
|
||||
import { TimelineKeyframeStyle } from './timelineKeyframeStyle';
|
||||
|
||||
/**
|
||||
* Style of the row.
|
||||
*/
|
||||
export interface TimelineRowStyle {
|
||||
/**
|
||||
* Size of the row in pixels.
|
||||
*/
|
||||
height?: number;
|
||||
/**
|
||||
* Track fill color.
|
||||
*/
|
||||
fillColor?: string;
|
||||
/**
|
||||
* Row margin bottom in pixels between tracks/rows.
|
||||
*/
|
||||
marginBottom?: number;
|
||||
/**
|
||||
* Style for the all keyframes in a current row.
|
||||
* Individual keyframe can have own style.
|
||||
*/
|
||||
keyframesStyle?: TimelineKeyframeStyle;
|
||||
/**
|
||||
* Style of the groups. Keyframe groups can be also styles separately.
|
||||
*/
|
||||
groupsStyle?: TimelineGroupStyle;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { TimelineCursorType } from '../../enums/timelineCursorType';
|
||||
import { TimelineCapStyle } from './timelineCapStyle';
|
||||
|
||||
/**
|
||||
* Timeline active/current value indicator style.
|
||||
*/
|
||||
export interface TimelineStyle {
|
||||
width?: number;
|
||||
/**
|
||||
* Margin top in pixels.
|
||||
*/
|
||||
marginTop?: number;
|
||||
/**
|
||||
* Margin bottom in pixels.
|
||||
*/
|
||||
marginBottom?: number;
|
||||
/**
|
||||
* Timeline top cap style.
|
||||
*/
|
||||
capStyle?: TimelineCapStyle;
|
||||
/**
|
||||
* Timeline indicator stroke color.
|
||||
*/
|
||||
strokeColor?: string;
|
||||
/**
|
||||
* Timeline fill color.
|
||||
*/
|
||||
fillColor?: string;
|
||||
/**
|
||||
* Timeline cursor.
|
||||
*/
|
||||
cursor?: TimelineCursorType;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Internal components consts.
|
||||
* Still can be changed thru private property _consts but this is discouraged.
|
||||
*/
|
||||
export interface TimelineConsts {
|
||||
/**
|
||||
* Auto pan speed.
|
||||
*/
|
||||
autoPanSpeed: number;
|
||||
/**
|
||||
* scroll speed when mouse drag is used (from 0 to 1)
|
||||
*/
|
||||
scrollByDragSpeed: number;
|
||||
/**
|
||||
* Determine whether item was clicked.
|
||||
*/
|
||||
clickDetectionMs: number;
|
||||
/**
|
||||
* Timeout to detect double click.
|
||||
*/
|
||||
doubleClickTimeoutMs: number;
|
||||
/**
|
||||
* Time in ms used to refresh scrollbars when pan is finished.
|
||||
*/
|
||||
scrollFinishedTimeoutMs: number;
|
||||
/**
|
||||
* Auto pan padding
|
||||
*/
|
||||
autoPanByScrollPadding: number;
|
||||
/**
|
||||
* Click threshold
|
||||
*/
|
||||
clickThreshold: number;
|
||||
|
||||
/**
|
||||
* Click min radius for the elements detection.
|
||||
*/
|
||||
clickDetectionMinRadius: number;
|
||||
/**
|
||||
* Default auto size for the group. It's percents.
|
||||
*/
|
||||
autoPanSpeedLimit: number;
|
||||
/**
|
||||
* Default auto size for the group. It's percents.
|
||||
*/
|
||||
defaultGroupHeight: number;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { TimelineRowStyle } from './styles/timelineRowStyle';
|
||||
import { TimelineStyle } from './styles/timelineStyle';
|
||||
import { TimelineRanged } from '../models/timelineRanged';
|
||||
|
||||
export interface TimelineOptions extends TimelineRanged {
|
||||
/**
|
||||
* Id 或时间轴容器的 HTMLElement
|
||||
*/
|
||||
id?: string | HTMLElement | null;
|
||||
/**
|
||||
* 检查是否启用了捕捉
|
||||
*/
|
||||
snapEnabled?: boolean;
|
||||
/**
|
||||
* 拖动时将所有选定的关键帧合并为一个包.
|
||||
*/
|
||||
snapAllKeyframesOnMove?: boolean;
|
||||
/**
|
||||
* 以像素为单位的时间轴的近似步长为1秒
|
||||
*/
|
||||
stepPx?: number;
|
||||
/**
|
||||
* 一个步骤中应该包含的点的个数。
|
||||
*/
|
||||
stepVal?: number;
|
||||
stepSmallPx?: number;
|
||||
/**
|
||||
* 以单位表示。从0到stepVal
|
||||
*/
|
||||
snapStep?: number;
|
||||
/**
|
||||
* 以像素为单位增加左页边距,从该值开始计算行距.
|
||||
*/
|
||||
leftMargin?: number;
|
||||
/**
|
||||
* 组件header背景颜色.
|
||||
*/
|
||||
headerFillColor?: string;
|
||||
/**
|
||||
* 组件背景颜色.
|
||||
*/
|
||||
fillColor?: string;
|
||||
/**
|
||||
* 标题标签颜色.
|
||||
*/
|
||||
labelsColor?: string;
|
||||
/**
|
||||
* 标题刻度条颜色.
|
||||
*/
|
||||
tickColor?: string;
|
||||
/**
|
||||
* 选择矩形颜色.
|
||||
*/
|
||||
selectionColor?: string;
|
||||
|
||||
/**
|
||||
* 标题高度(以像素为单位)。
|
||||
*/
|
||||
headerHeight?: number;
|
||||
/**
|
||||
* Header ticks font
|
||||
*/
|
||||
font?: string;
|
||||
/**
|
||||
* Default zoom level = 1. where screen pixels are equals to the corresponding stepVal stepPx.
|
||||
*/
|
||||
zoom?: number;
|
||||
/**
|
||||
* Default zoom speed.
|
||||
*/
|
||||
zoomSpeed?: number;
|
||||
/**
|
||||
* Max zoom value.
|
||||
*/
|
||||
zoomMin?: number;
|
||||
/**
|
||||
* Min zoom value.
|
||||
*/
|
||||
zoomMax?: number;
|
||||
/**
|
||||
* Set this to true in a MAC OS environment: The Meta key will be used instead of the Ctrl key.
|
||||
*/
|
||||
controlKeyIsMetaKey?: boolean;
|
||||
/**
|
||||
* Access the scroll container via this class for e.g. scroll bar styling.
|
||||
*/
|
||||
scrollContainerClass?: string;
|
||||
/**
|
||||
* Default rows style.
|
||||
* Can be overridden by setting style individually for each row.
|
||||
*/
|
||||
rowsStyle?: TimelineRowStyle;
|
||||
/**
|
||||
* Timeline indicator style.
|
||||
*/
|
||||
timelineStyle?: TimelineStyle;
|
||||
|
||||
/**
|
||||
* keyframes group is draggable. Default: true
|
||||
*/
|
||||
groupsDraggable?: boolean;
|
||||
/**
|
||||
* keyframes group is draggable. Default: true
|
||||
*/
|
||||
keyframesDraggable?: boolean;
|
||||
/**
|
||||
* Timeline can be dragged or position can be changed by user interaction. Default: true
|
||||
*/
|
||||
timelineDraggable?: boolean;
|
||||
|
||||
/**
|
||||
* Array of the denominators used to determine 'beautiful' numbers to be rendered for the gauge.
|
||||
* Default: [1, 2, 5, 10];
|
||||
*/
|
||||
denominators?: number[];
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
interface TimelineEvent {
|
||||
topic: string;
|
||||
callback: (args: any) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeline events emitter.
|
||||
*/
|
||||
export class TimelineEventsEmitter {
|
||||
/**
|
||||
* Active events subscriptions.
|
||||
*/
|
||||
_subscriptions: TimelineEvent[] = [];
|
||||
|
||||
/**
|
||||
* Subscribe event.
|
||||
* @param topic event name.
|
||||
* @param callback callback to be added.
|
||||
*/
|
||||
on<T>(topic: string, callback: (args: T) => void): boolean {
|
||||
if (!callback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._subscriptions.push({
|
||||
topic: topic,
|
||||
callback: callback,
|
||||
} as TimelineEvent);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Remove an event from the subscriptions list.
|
||||
*/
|
||||
off<T>(topic: string, callback: (args: T) => void): boolean {
|
||||
const before = this._subscriptions.length;
|
||||
this._subscriptions = this._subscriptions.filter((event) => {
|
||||
return event && event.callback != callback && event.topic != topic;
|
||||
});
|
||||
return before !== this._subscriptions.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe all
|
||||
*/
|
||||
offAll(): void {
|
||||
// Remove all callbacks from array.
|
||||
this._subscriptions.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit event.
|
||||
* @param topic Event name.
|
||||
* @param args Event arguments.
|
||||
*/
|
||||
emit<T>(topic: string, args: T): void {
|
||||
this._subscriptions.forEach((event) => {
|
||||
if (event?.topic === topic && event?.callback) {
|
||||
event.callback(args);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Timeline base preventable event.
|
||||
*/
|
||||
export class TimelineBaseEvent {
|
||||
private _prevented = false;
|
||||
/**
|
||||
* Prevent default click logic.
|
||||
*/
|
||||
preventDefault(): void {
|
||||
this._prevented = true;
|
||||
}
|
||||
|
||||
isPrevented(): boolean {
|
||||
return this._prevented;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { TimelineElement } from '../timelineElement';
|
||||
import { TimelinePoint } from '../timelinePoint';
|
||||
import { TimelineBaseEvent } from './timelineBaseEvent';
|
||||
|
||||
export class TimelineClickEvent extends TimelineBaseEvent {
|
||||
args: MouseEvent | TouchEvent | null = null;
|
||||
/**
|
||||
* Clicked screen position.
|
||||
*/
|
||||
get pos(): DOMPoint | null {
|
||||
return this.point?.pos || null;
|
||||
}
|
||||
get val(): number {
|
||||
if (this.point) {
|
||||
return this.point.val;
|
||||
}
|
||||
return NaN;
|
||||
}
|
||||
/**
|
||||
* All elements located under current mouse activity.
|
||||
*/
|
||||
elements: TimelineElement[] = [];
|
||||
/**
|
||||
* Element that selected as target under the click.
|
||||
*/
|
||||
target: TimelineElement | null = null;
|
||||
/**
|
||||
* Timeline current active drag position.
|
||||
*/
|
||||
point: TimelinePoint | null = null;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { TimelinePoint } from '../timelinePoint';
|
||||
import { TimelineBaseEvent } from './timelineBaseEvent';
|
||||
import { TimelineElementDragState } from '../timelineElementDragState';
|
||||
|
||||
export class TimelineDragEvent extends TimelineBaseEvent {
|
||||
args: MouseEvent | TouchEvent | null = null;
|
||||
get pos(): DOMPoint | null {
|
||||
return this.point?.pos || null;
|
||||
}
|
||||
/**
|
||||
* Elements to be dragged as a group.
|
||||
*/
|
||||
elements: TimelineElementDragState[] | null = null;
|
||||
/**
|
||||
* Target element
|
||||
*/
|
||||
target: TimelineElementDragState | null = null;
|
||||
/**
|
||||
* Timeline current active drag position.
|
||||
*/
|
||||
point: TimelinePoint | null = null;
|
||||
/**
|
||||
* Timeline previous drag position.
|
||||
*/
|
||||
prevPoint: TimelinePoint | null = null;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { TimelineBaseEvent } from './timelineBaseEvent';
|
||||
import { TimelineElement } from '../timelineElement';
|
||||
import { TimelineEventSource } from '../../enums/timelineEventSource';
|
||||
|
||||
export class TimelineKeyframeChangedEvent extends TimelineBaseEvent {
|
||||
/**
|
||||
* Value to be used.
|
||||
*/
|
||||
val!: number;
|
||||
/**
|
||||
* Previous value.
|
||||
*/
|
||||
prevVal!: number;
|
||||
/**
|
||||
* Target element
|
||||
*/
|
||||
target!: TimelineElement;
|
||||
/**
|
||||
* Event source.
|
||||
*/
|
||||
source!: TimelineEventSource;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface TimelineScrollEvent {
|
||||
args: MouseEvent;
|
||||
/**
|
||||
* Whether scroll was component or user initiated.
|
||||
*/
|
||||
scrollProgrammatically: boolean;
|
||||
scrollLeft: number;
|
||||
scrollTop: number;
|
||||
scrollHeight: number;
|
||||
scrollWidth: number;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TimelineSelectionEventSource } from '../../enums/timelineSelectionEventSource';
|
||||
import { TimelineKeyframe } from '../../models/timelineKeyframe';
|
||||
|
||||
export class TimelineSelectedEvent {
|
||||
/**
|
||||
* Selected Keyframes list.
|
||||
*/
|
||||
selected: TimelineKeyframe[] = [];
|
||||
/**
|
||||
* Changed selection.
|
||||
*/
|
||||
changed: TimelineKeyframe[] = [];
|
||||
/**
|
||||
* Selection mode.
|
||||
*/
|
||||
mode: TimelineSelectionEventSource = TimelineSelectionEventSource.Keyframes;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TimelineBaseEvent } from './timelineBaseEvent';
|
||||
import { TimelineEventSource } from '../../enums/timelineEventSource';
|
||||
|
||||
export class TimelineTimeChangedEvent extends TimelineBaseEvent {
|
||||
/**
|
||||
* New value to be set if not prevented.
|
||||
*/
|
||||
val = 0;
|
||||
/**
|
||||
* previous value, that actually set at the moment.
|
||||
*/
|
||||
prevVal = 0;
|
||||
/**
|
||||
* Source of the change.
|
||||
*/
|
||||
source: TimelineEventSource = TimelineEventSource.User;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Helper container. Cut bounds method results.
|
||||
*/
|
||||
export interface TimelineCutBoundsRectResults {
|
||||
rect: DOMRect;
|
||||
overlapY: boolean;
|
||||
overlapX: boolean;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { TimelineElementType } from '../enums/timelineElementType';
|
||||
import { TimelineElementDragState } from './timelineElementDragState';
|
||||
|
||||
/**
|
||||
* Information about current drag state.
|
||||
*/
|
||||
export class TimelineDraggableData {
|
||||
/**
|
||||
* Whether position was changed.
|
||||
*/
|
||||
changed = false;
|
||||
/**
|
||||
* Drag initial click target.
|
||||
*/
|
||||
target!: TimelineElementDragState;
|
||||
/**
|
||||
* Elements to be dragged.
|
||||
* Can be multiple elements, but drag will be started with target.
|
||||
*/
|
||||
elements!: TimelineElementDragState[];
|
||||
/**
|
||||
* Dragging type.
|
||||
*/
|
||||
type: TimelineElementType = TimelineElementType.None;
|
||||
/**
|
||||
* Prev value.
|
||||
*/
|
||||
val!: number;
|
||||
/**
|
||||
* Prev value.
|
||||
*/
|
||||
prevVal!: number;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { TimelineKeyframe } from '../models/timelineKeyframe';
|
||||
import { TimelineElementType } from '../enums/timelineElementType';
|
||||
import { TimelineRow } from '../models/timelineRow';
|
||||
import { TimelineGroup } from '../models/timelineGroup';
|
||||
|
||||
/**
|
||||
* Timeline clickable element.
|
||||
*/
|
||||
export interface TimelineElement {
|
||||
type: TimelineElementType;
|
||||
/**
|
||||
* Timeline value,
|
||||
*/
|
||||
val: number;
|
||||
|
||||
/**
|
||||
* Related keyframe model.
|
||||
*/
|
||||
keyframe?: TimelineKeyframe;
|
||||
|
||||
/**
|
||||
* Related row model.
|
||||
*/
|
||||
row?: TimelineRow;
|
||||
|
||||
/**
|
||||
* Current group.
|
||||
*/
|
||||
group?: string | TimelineGroup;
|
||||
/**
|
||||
* List of the selected, grouped, row keyframes.
|
||||
*/
|
||||
keyframes?: TimelineKeyframe[];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { TimelineElement } from './timelineElement';
|
||||
|
||||
export interface TimelineElementDragState extends TimelineElement {
|
||||
/**
|
||||
* Value that preserved before dragging is started.
|
||||
*/
|
||||
startedVal: number;
|
||||
/**
|
||||
* Value before change.
|
||||
*/
|
||||
prevVal: number;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { TimelinePoint } from './timelinePoint';
|
||||
|
||||
/**
|
||||
* Mouse data helper container.
|
||||
*/
|
||||
export interface TimelineMouseData extends TimelinePoint {
|
||||
/**
|
||||
* Originated event args.
|
||||
*/
|
||||
args: TouchEvent | MouseEvent;
|
||||
/**
|
||||
* Click radius.
|
||||
*/
|
||||
radius?: number;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Timeline values interface.
|
||||
*/
|
||||
export interface TimelinePoint {
|
||||
/**
|
||||
* Value used for the visualization.
|
||||
* It can be snapped or original.
|
||||
*/
|
||||
val: number;
|
||||
/**
|
||||
* Snapped value if snapping is enabled. In other case corresponding to original value.
|
||||
*/
|
||||
snapVal: number;
|
||||
/**
|
||||
* Unsnapped original value.
|
||||
*/
|
||||
originalVal: number;
|
||||
/**
|
||||
* Screen click point.
|
||||
*/
|
||||
pos: DOMPoint;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Timeline selectable element.
|
||||
*/
|
||||
export interface TimelineSelectable {
|
||||
selected?: boolean;
|
||||
selectable?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TimelineKeyframe } from '../models/timelineKeyframe';
|
||||
|
||||
export interface TimelineSelectionResults {
|
||||
/**
|
||||
* Changed nodes.
|
||||
*/
|
||||
changed: TimelineKeyframe[];
|
||||
/**
|
||||
* Selected nodes.
|
||||
*/
|
||||
selected: TimelineKeyframe[];
|
||||
/**
|
||||
* Whether data was changed.
|
||||
*/
|
||||
selectionChanged: boolean;
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
import { TimelineOptions } from '../settings/timelineOptions';
|
||||
import { TimelineRowStyle } from '../settings/styles/timelineRowStyle';
|
||||
import { TimelineKeyframeShape } from '../enums/timelineKeyframeShape';
|
||||
import { TimelineUtils } from './timelineUtils';
|
||||
import { TimelineGroupStyle } from '../settings/styles/timelineGroupStyle';
|
||||
import { TimelineKeyframe } from '../models/timelineKeyframe';
|
||||
|
||||
import { TimelineGroup } from '../models/timelineGroup';
|
||||
import { TimelineRow } from '../models/timelineRow';
|
||||
import { defaultGroupStyle } from '../settings/defaults/defaultGroupStyle';
|
||||
import { defaultTimelineKeyframeStyle } from '../settings/defaults/defaultTimelineKeyframeStyle';
|
||||
import { defaultTimelineRowStyle } from '../settings/defaults/defaultTimelineRowStyle';
|
||||
|
||||
const undefinedConst = typeof undefined;
|
||||
|
||||
// TODO: create merged style for each element instead of getting per property.
|
||||
export class TimelineStyleUtils {
|
||||
static getGroup(groupModel: TimelineGroup | string | null | undefined): TimelineGroup | null {
|
||||
const style = groupModel;
|
||||
if (style && typeof style === 'string') {
|
||||
return null;
|
||||
}
|
||||
return (style || null) as TimelineGroup;
|
||||
}
|
||||
static getGroupStyle(groupModel: TimelineGroup | string | null | undefined): TimelineGroupStyle | null {
|
||||
return TimelineStyleUtils.getGroup(groupModel)?.style || null;
|
||||
}
|
||||
static getFirstSet<T>(defaultValue: T, ...params: Array<T | undefined | null>): T {
|
||||
return TimelineStyleUtils.getValue(defaultValue, false, ...params);
|
||||
}
|
||||
/**
|
||||
* Get first value set or default.
|
||||
* @param defaultValue default value in a case when no value is set.
|
||||
* @param returnFalseIfAnyFalse - find first negative bool and return false.
|
||||
* @param params collection of values to check.
|
||||
* @returns value.
|
||||
*/
|
||||
static getValue<T>(defaultValue: T, returnFalseIfAnyFalse = false, ...params: Array<T | undefined | null>): T {
|
||||
const valuesFound: T[] = [];
|
||||
let found = false;
|
||||
params.forEach((value: T | undefined | null) => {
|
||||
if (found) {
|
||||
return;
|
||||
}
|
||||
if (typeof value === undefinedConst) {
|
||||
return;
|
||||
} else if (typeof value === 'number') {
|
||||
if (!TimelineUtils.isNumber(value)) {
|
||||
return;
|
||||
}
|
||||
} else if (typeof value === 'boolean') {
|
||||
valuesFound.push(value as T);
|
||||
// No need to search for other values. First false is turning off current bool functionality.
|
||||
if (returnFalseIfAnyFalse && value === false) {
|
||||
found = true;
|
||||
}
|
||||
return;
|
||||
} else if (!value) {
|
||||
return;
|
||||
}
|
||||
valuesFound.push(value as T);
|
||||
});
|
||||
const toReturn = valuesFound && valuesFound.length > 0 ? valuesFound[0] : defaultValue;
|
||||
if (found) {
|
||||
return false as T;
|
||||
}
|
||||
return TimelineStyleUtils.getValueOrDefault(toReturn, defaultValue) as T;
|
||||
}
|
||||
|
||||
static getValueOrDefault<T>(value: T, defaultValue: T): T | undefined {
|
||||
if (typeof value === undefinedConst) {
|
||||
return defaultValue;
|
||||
} else if (typeof value == 'boolean') {
|
||||
// variable is a boolean
|
||||
return value as T;
|
||||
} else if (typeof value == 'number') {
|
||||
// variable is a boolean
|
||||
if (value || value === 0) {
|
||||
return value as T;
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return value || defaultValue;
|
||||
}
|
||||
static keyframeWidth(
|
||||
keyframe: TimelineKeyframe | null | undefined,
|
||||
group: TimelineGroup | string | null | undefined,
|
||||
rowStyle: TimelineRowStyle | null | undefined,
|
||||
options: TimelineOptions | null | undefined,
|
||||
): number | string {
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultTimelineKeyframeStyle.width || '',
|
||||
keyframe?.style?.width,
|
||||
// exact style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.keyframesStyle?.width,
|
||||
rowStyle?.keyframesStyle?.width,
|
||||
rowStyle?.groupsStyle?.keyframesStyle?.width,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.keyframesStyle?.width,
|
||||
// default keyframe style
|
||||
options?.rowsStyle?.keyframesStyle?.width,
|
||||
);
|
||||
}
|
||||
static keyframeHeight(
|
||||
keyframe: TimelineKeyframe | null | undefined,
|
||||
group: TimelineGroup | string | null | undefined,
|
||||
rowStyle: TimelineRowStyle | null | undefined,
|
||||
options: TimelineOptions | null | undefined,
|
||||
): number | string {
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultTimelineKeyframeStyle.height || '',
|
||||
keyframe?.style?.height,
|
||||
// exact style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.keyframesStyle?.height,
|
||||
rowStyle?.keyframesStyle?.height,
|
||||
rowStyle?.groupsStyle?.keyframesStyle?.height,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.keyframesStyle?.height,
|
||||
// default keyframe style
|
||||
options?.rowsStyle?.keyframesStyle?.height,
|
||||
);
|
||||
}
|
||||
static keyframeShape(keyframe: TimelineKeyframe | null, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): TimelineKeyframeShape {
|
||||
const defaultValue = defaultTimelineKeyframeStyle.shape || TimelineKeyframeShape.Rhomb;
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
keyframe?.style?.shape,
|
||||
// style set by keyframe group
|
||||
TimelineStyleUtils.getGroupStyle(group)?.keyframesStyle?.shape,
|
||||
// style from the keyframe group
|
||||
rowStyle?.groupsStyle?.keyframesStyle?.shape,
|
||||
// style set by keyframe style
|
||||
rowStyle?.keyframesStyle?.shape,
|
||||
// style set by keyframe group style, applied when group is set
|
||||
group ? options?.rowsStyle?.groupsStyle?.keyframesStyle?.shape : undefined,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.keyframesStyle?.shape,
|
||||
);
|
||||
}
|
||||
static keyframeFillColor(keyframe: TimelineKeyframe | null, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): string {
|
||||
const defaultValue = defaultTimelineKeyframeStyle.fillColor || '';
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
keyframe?.style?.fillColor,
|
||||
// style set by keyframe group
|
||||
TimelineStyleUtils.getGroupStyle(group)?.keyframesStyle?.fillColor,
|
||||
// style from the keyframe group
|
||||
rowStyle?.groupsStyle?.keyframesStyle?.fillColor,
|
||||
// style set by keyframe style
|
||||
rowStyle?.keyframesStyle?.fillColor,
|
||||
// style set by keyframe group style, applied when group is set
|
||||
group ? options?.rowsStyle?.groupsStyle?.keyframesStyle?.fillColor : undefined,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.keyframesStyle?.fillColor,
|
||||
);
|
||||
}
|
||||
static keyframeSelectedFillColor(keyframe: TimelineKeyframe | null, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): string {
|
||||
const defaultValue = defaultTimelineKeyframeStyle.selectedFillColor || '';
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
keyframe?.style?.selectedFillColor,
|
||||
// style set by keyframe group
|
||||
TimelineStyleUtils.getGroupStyle(group)?.keyframesStyle?.selectedFillColor,
|
||||
// style from the keyframe group
|
||||
rowStyle?.groupsStyle?.keyframesStyle?.selectedFillColor,
|
||||
// style set by keyframe style
|
||||
rowStyle?.keyframesStyle?.selectedFillColor,
|
||||
// style set by keyframe group style, applied when group is set
|
||||
group ? options?.rowsStyle?.groupsStyle?.keyframesStyle?.selectedFillColor : undefined,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.keyframesStyle?.selectedFillColor,
|
||||
);
|
||||
}
|
||||
|
||||
static keyframeStrokeThickness(keyframe: TimelineKeyframe | null, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): number {
|
||||
const defaultValue = defaultTimelineKeyframeStyle.strokeThickness || 0;
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
keyframe?.style?.strokeThickness,
|
||||
// style set by keyframe group
|
||||
TimelineStyleUtils.getGroupStyle(group)?.keyframesStyle?.strokeThickness,
|
||||
// style from the keyframe group
|
||||
rowStyle?.groupsStyle?.keyframesStyle?.strokeThickness,
|
||||
// style set by keyframe style
|
||||
rowStyle?.keyframesStyle?.strokeThickness,
|
||||
// style set by keyframe group style, applied when group is set
|
||||
group ? options?.rowsStyle?.groupsStyle?.keyframesStyle?.strokeThickness : undefined,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.keyframesStyle?.strokeThickness,
|
||||
);
|
||||
}
|
||||
|
||||
static keyframeStrokeColor(keyframe: TimelineKeyframe | null, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): string {
|
||||
const defaultValue = defaultTimelineKeyframeStyle.strokeColor || '';
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
keyframe?.style?.strokeColor,
|
||||
// style set by keyframe group
|
||||
TimelineStyleUtils.getGroupStyle(group)?.keyframesStyle?.strokeColor,
|
||||
// style from the keyframe group
|
||||
rowStyle?.groupsStyle?.keyframesStyle?.strokeColor,
|
||||
// style set by keyframe style
|
||||
rowStyle?.keyframesStyle?.strokeColor,
|
||||
// style set by keyframe group style, applied when group is set
|
||||
group ? options?.rowsStyle?.groupsStyle?.keyframesStyle?.strokeColor : undefined,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.keyframesStyle?.strokeColor,
|
||||
);
|
||||
}
|
||||
static keyframeSelectedStrokeColor(keyframe: TimelineKeyframe | null, group: TimelineGroup | string | null, rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): string {
|
||||
const defaultValue = defaultTimelineKeyframeStyle.selectedStrokeColor || '';
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
keyframe?.style?.selectedStrokeColor,
|
||||
// style set by keyframe group
|
||||
TimelineStyleUtils.getGroupStyle(group)?.keyframesStyle?.selectedStrokeColor,
|
||||
// style from the keyframe group
|
||||
rowStyle?.groupsStyle?.keyframesStyle?.selectedStrokeColor,
|
||||
// style set by keyframe style
|
||||
rowStyle?.keyframesStyle?.selectedStrokeColor,
|
||||
// style set by keyframe group style, applied when group is set
|
||||
group ? options?.rowsStyle?.groupsStyle?.keyframesStyle?.selectedStrokeColor : undefined,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.keyframesStyle?.selectedStrokeColor,
|
||||
);
|
||||
}
|
||||
|
||||
static groupHeight(options: TimelineOptions | null | undefined, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null | undefined): number | string {
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultGroupStyle.height || 'auto',
|
||||
// exact group style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.height,
|
||||
// Row row style
|
||||
rowStyle?.groupsStyle?.height,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.height,
|
||||
);
|
||||
}
|
||||
|
||||
static groupMarginTop(options: TimelineOptions | null | undefined, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null | undefined): number | string {
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultGroupStyle.marginTop || 'auto',
|
||||
// exact style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.marginTop,
|
||||
// Row row style
|
||||
rowStyle?.groupsStyle?.marginTop,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.marginTop,
|
||||
);
|
||||
}
|
||||
|
||||
static groupFillColor(options: TimelineOptions | null | undefined, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null | undefined): string {
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultGroupStyle.fillColor || '',
|
||||
// exact style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.fillColor,
|
||||
// Row row style
|
||||
rowStyle?.groupsStyle?.fillColor,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.fillColor,
|
||||
);
|
||||
}
|
||||
static groupStrokeColor(options: TimelineOptions | null | undefined, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null | undefined): string {
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultGroupStyle.strokeColor || '',
|
||||
// exact style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.strokeColor,
|
||||
// Row row style
|
||||
rowStyle?.groupsStyle?.strokeColor,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.strokeColor,
|
||||
);
|
||||
}
|
||||
|
||||
static groupStrokeThickness(options: TimelineOptions | null | undefined, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null | undefined): number {
|
||||
return (
|
||||
TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultGroupStyle.strokeThickness || '',
|
||||
// exact style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.strokeThickness,
|
||||
// Row row style
|
||||
rowStyle?.groupsStyle?.strokeThickness,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.strokeThickness,
|
||||
) || 0
|
||||
);
|
||||
}
|
||||
|
||||
static groupsRadii(
|
||||
options: TimelineOptions | null | undefined,
|
||||
group: TimelineGroup | string | null | undefined,
|
||||
rowStyle: TimelineRowStyle | null | undefined,
|
||||
): number | DOMPointInit | Iterable<number | DOMPointInit> {
|
||||
return (
|
||||
TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultGroupStyle.radii || '',
|
||||
// exact style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.radii,
|
||||
// Row row style
|
||||
rowStyle?.groupsStyle?.radii,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.radii,
|
||||
) || 0
|
||||
);
|
||||
}
|
||||
|
||||
static groupText(options: TimelineOptions | null | undefined, group: TimelineGroup | string | null | undefined, rowStyle: TimelineRowStyle | null | undefined): TimelineGroupStyle["text"] {
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultGroupStyle.text || {
|
||||
label:""
|
||||
},
|
||||
// exact style
|
||||
TimelineStyleUtils.getGroupStyle(group)?.text,
|
||||
// Row row style
|
||||
rowStyle?.groupsStyle?.text,
|
||||
// global styles
|
||||
options?.rowsStyle?.groupsStyle?.text,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current row height from styles
|
||||
*/
|
||||
static getRowHeight(rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): number {
|
||||
const defaultValue = defaultTimelineRowStyle.height || 0;
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
rowStyle?.height,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.height,
|
||||
);
|
||||
}
|
||||
static getRowMarginBottom(rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): number {
|
||||
const defaultValue = defaultTimelineRowStyle.marginBottom || 0;
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
rowStyle?.marginBottom,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.marginBottom,
|
||||
);
|
||||
}
|
||||
|
||||
static getRowFillColor(rowStyle: TimelineRowStyle | null, options: TimelineOptions | null): string {
|
||||
const defaultValue = defaultTimelineRowStyle.fillColor || '';
|
||||
return TimelineStyleUtils.getFirstSet(
|
||||
// default value
|
||||
defaultValue,
|
||||
// exact style
|
||||
rowStyle?.fillColor,
|
||||
// Style set by global options
|
||||
options?.rowsStyle?.fillColor,
|
||||
);
|
||||
}
|
||||
|
||||
static headerHeight(options: TimelineOptions | null, defaultRowHeight = 30): number {
|
||||
return options?.headerHeight || defaultRowHeight;
|
||||
}
|
||||
static keyframeDraggable(keyframe: TimelineKeyframe | null, group: TimelineGroup | string | null, row: TimelineRow | null, options: TimelineOptions | null, defaultValue = true): boolean {
|
||||
const findFirstNegativeBool = true;
|
||||
const boolResult = TimelineStyleUtils.getValue<boolean>(
|
||||
defaultValue,
|
||||
findFirstNegativeBool,
|
||||
// Keyframe settings
|
||||
keyframe?.draggable,
|
||||
// Group settings
|
||||
TimelineStyleUtils.getGroup(group)?.keyframesDraggable,
|
||||
// Row settings
|
||||
row?.keyframesDraggable,
|
||||
// Start from global settings first.
|
||||
options?.keyframesDraggable,
|
||||
);
|
||||
return boolResult;
|
||||
}
|
||||
|
||||
static groupDraggable(group: TimelineGroup | string | null | undefined, row: TimelineRow | null, options: TimelineOptions): boolean {
|
||||
const findFirstNegativeBool = true;
|
||||
const boolResult = TimelineStyleUtils.getValue<boolean>(
|
||||
true,
|
||||
findFirstNegativeBool,
|
||||
// Group settings
|
||||
TimelineStyleUtils.getGroup(group)?.draggable,
|
||||
// Row settings
|
||||
row?.groupsDraggable,
|
||||
// Start from global settings first.
|
||||
options?.groupsDraggable,
|
||||
);
|
||||
return boolResult;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
import { TimelineRanged } from '../models/timelineRanged';
|
||||
import { TimelineOptions } from '../settings/timelineOptions';
|
||||
|
||||
const defaultDenominators = [1, 2, 5, 10];
|
||||
export class TimelineUtils {
|
||||
static drawLine(ctx: CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number): void {
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
}
|
||||
/**
|
||||
* Check is valid number.
|
||||
*/
|
||||
static isNumber(val?: number | null): boolean {
|
||||
if (typeof val === 'number' && (val || val === 0) && !isNaN(val) && Number.isFinite(val)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
static deleteElement<T>(array: Array<T>, element: T): Array<T> {
|
||||
const index: number = array.indexOf(element);
|
||||
if (index !== -1) {
|
||||
return array.splice(index, 1);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check rectangle overlap x,y
|
||||
*/
|
||||
static isOverlap(x: number, y: number, rectangle: DOMRect): boolean {
|
||||
if (!rectangle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rectangle.x <= x && rectangle.x + rectangle.width >= x && rectangle.y <= y && rectangle.y + rectangle.height >= y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Find beautiful step for the header line gauge.
|
||||
*/
|
||||
static findGoodStep(originalStep: number, divisionCheck = 0, denominators: number[] = defaultDenominators): number {
|
||||
if (!denominators) {
|
||||
denominators = defaultDenominators;
|
||||
}
|
||||
if (originalStep <= 0 || isNaN(originalStep) || !Number.isFinite(originalStep)) {
|
||||
return originalStep;
|
||||
}
|
||||
let step = originalStep;
|
||||
let lastDistance:number | null = null;
|
||||
const pow = TimelineUtils.getPowArgument(originalStep);
|
||||
for (let i = 0; i < denominators.length; i++) {
|
||||
const denominator = denominators[i];
|
||||
const calculatedStep = denominator * Math.pow(10, pow);
|
||||
if (divisionCheck && divisionCheck % calculatedStep != 0) {
|
||||
continue;
|
||||
}
|
||||
const distance = TimelineUtils.getDistance(originalStep, calculatedStep);
|
||||
|
||||
if (distance == 0 || (distance <= 0.1 && pow > 0)) {
|
||||
lastDistance = distance;
|
||||
step = calculatedStep;
|
||||
break;
|
||||
} else if (!lastDistance || lastDistance > distance) {
|
||||
lastDistance = distance;
|
||||
step = calculatedStep;
|
||||
}
|
||||
}
|
||||
|
||||
return step;
|
||||
}
|
||||
/**
|
||||
* Keep value in min, max bounds.
|
||||
*/
|
||||
static keepInBounds(value: number, min: number | null | undefined = null, max: number | null | undefined = null): number {
|
||||
if (TimelineUtils.isNumber(value)) {
|
||||
if (TimelineUtils.isNumber(min) && (min || min === 0)) {
|
||||
value = Math.max(value, min);
|
||||
}
|
||||
if (TimelineUtils.isNumber(max) && (max || max === 0)) {
|
||||
value = Math.min(value, max);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
static setMinMax(to: TimelineRanged, from: TimelineRanged | null, shrink = false): TimelineRanged {
|
||||
if (!from || !to) {
|
||||
return to;
|
||||
}
|
||||
const fromMin = from ? from.min : Number.NaN;
|
||||
const toMin = to.min;
|
||||
const isFromMinNumber = (fromMin || fromMin === 0) && TimelineUtils.isNumber(fromMin);
|
||||
const isToMinNumber = (toMin || toMin === 0) && TimelineUtils.isNumber(toMin);
|
||||
// get absolute min and max bounds:
|
||||
if (isFromMinNumber && isToMinNumber) {
|
||||
to.min = shrink ? Math.min(fromMin, toMin) : Math.max(fromMin, toMin);
|
||||
} else if (isFromMinNumber) {
|
||||
to.min = fromMin;
|
||||
}
|
||||
const fromMax = from ? from.max : Number.NaN;
|
||||
const toMax = to.max;
|
||||
const isFromMaxNumber = (fromMax || fromMax === 0) && TimelineUtils.isNumber(fromMax);
|
||||
const isToMaxNumber = (toMax || toMax === 0) && TimelineUtils.isNumber(toMax);
|
||||
if (isFromMaxNumber && isToMaxNumber) {
|
||||
to.max = shrink ? Math.max(fromMax, toMax) : Math.min(fromMax, toMax);
|
||||
} else if (isFromMaxNumber) {
|
||||
to.max = fromMax;
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
static shrinkSelf(rect: DOMRect, value: number): DOMRect {
|
||||
if (!rect) {
|
||||
return rect;
|
||||
}
|
||||
rect.x -= value;
|
||||
rect.y -= value;
|
||||
rect.width += value;
|
||||
rect.height += value;
|
||||
return rect;
|
||||
}
|
||||
/**
|
||||
* Check whether rectangle intersects another rectangle
|
||||
*/
|
||||
static isRectIntersects(rect: DOMRect, rect2: DOMRect, touch = false): boolean {
|
||||
if (!rect || !rect2) {
|
||||
console.log('Rectangles cannot be empty');
|
||||
return false;
|
||||
}
|
||||
const right = rect2.x + rect2.width;
|
||||
const bottom = rect2.y + rect2.height;
|
||||
if (touch) {
|
||||
if (
|
||||
// Left
|
||||
rect.x <= right &&
|
||||
// Right
|
||||
rect2.x <= rect.x + rect.width &&
|
||||
// Top
|
||||
rect.y <= bottom &&
|
||||
// Bottom
|
||||
rect2.y <= rect.y + rect.height
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
// Left
|
||||
rect.x < right &&
|
||||
// Right
|
||||
rect2.x < rect.x + rect.width &&
|
||||
// Top
|
||||
rect.y < bottom &&
|
||||
// Bottom
|
||||
rect2.y < rect.y + rect.height
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getDistance(x1: number, y1: number, x2?: number, y2?: number): number {
|
||||
if (x2 != undefined && y2 != undefined) {
|
||||
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
|
||||
} else {
|
||||
return Math.abs(x1 - y1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sign of the number. 1 or -1.
|
||||
*/
|
||||
static sign(p: number): number {
|
||||
return p >= 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear browser text selection.
|
||||
*/
|
||||
static clearBrowserSelection(): void {
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
if (window.getSelection) {
|
||||
const selection = window.getSelection();
|
||||
if (selection) {
|
||||
selection.removeAllRanges();
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const doc: any = window.document;
|
||||
if (doc.selection) {
|
||||
doc.selection.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
static getPowArgument(toCheck: number): number {
|
||||
if (!toCheck || toCheck === 0 || !isFinite(toCheck)) {
|
||||
return 1;
|
||||
}
|
||||
// some optimization for numbers:
|
||||
if (toCheck >= 10 && toCheck < 100) {
|
||||
return 1;
|
||||
} else if (toCheck >= 100 && toCheck < 1000) {
|
||||
return 2;
|
||||
} else if (toCheck >= 1000 && toCheck < 10000) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
toCheck = Math.abs(toCheck);
|
||||
let category = 0;
|
||||
const s = this.sign(toCheck);
|
||||
if (toCheck > 1) {
|
||||
while (toCheck >= 1) {
|
||||
toCheck = Math.floor(toCheck / 10.0);
|
||||
category++;
|
||||
}
|
||||
|
||||
return s * category - 1;
|
||||
} else if (toCheck > 0.0) {
|
||||
// Get number of zeros before the number.
|
||||
const zerosCount = Math.floor(Math.log(toCheck) / Math.log(10) + 1) - 1;
|
||||
return zerosCount;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static deepClone = <T>(previousOptions: T): T => {
|
||||
return JSON.parse(JSON.stringify(previousOptions)) as T;
|
||||
};
|
||||
|
||||
static cloneOptions = (previousOptions: TimelineOptions): TimelineOptions => {
|
||||
const clonedValue = JSON.parse(
|
||||
JSON.stringify(previousOptions, (key, value) => {
|
||||
// No need to clone HTML element passed as ID.
|
||||
return key === 'id' ? undefined : value;
|
||||
}),
|
||||
);
|
||||
clonedValue.id = previousOptions.id;
|
||||
return clonedValue;
|
||||
};
|
||||
/**
|
||||
* Merge options. New keys will be added.
|
||||
*/
|
||||
static mergeOptions(previousOptions: TimelineOptions, newOptions: TimelineOptions): TimelineOptions {
|
||||
newOptions = newOptions || ({} as TimelineOptions);
|
||||
// Apply incoming options to default. (override default)
|
||||
// Deep clone default options:
|
||||
const toArg = TimelineUtils.cloneOptions(previousOptions);
|
||||
// Merge options with the default.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mergeOptionsDeep = (to: any, from: any): void => {
|
||||
if (!to || !from) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let key in from) {
|
||||
if (Object.prototype.hasOwnProperty.call(from, key)) {
|
||||
if (from[key] !== undefined) {
|
||||
if (typeof from[key] === 'object') {
|
||||
if (key === 'id') {
|
||||
if (from[key] && from[key] !== to[key]) {
|
||||
to[key] = from[key];
|
||||
}
|
||||
} else {
|
||||
if (!to[key]) {
|
||||
to[key] = from[key];
|
||||
} else {
|
||||
mergeOptionsDeep(to[key], from[key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
to[key] = from[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mergeOptionsDeep(toArg, newOptions);
|
||||
return toArg;
|
||||
}
|
||||
/**
|
||||
* Format numbers with len
|
||||
*/
|
||||
static timePadZero(num: number, len = 2): string {
|
||||
let str = String(num);
|
||||
const threshold = Math.pow(10, len - 1);
|
||||
if (num < threshold) {
|
||||
while (String(threshold).length > str.length) {
|
||||
str = `0${num}`;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { TimelineGroup } from '../models/timelineGroup';
|
||||
import { TimelineKeyframeViewModel } from './timelineKeyframeViewModel';
|
||||
|
||||
export interface TimelineGroupViewModel {
|
||||
/**
|
||||
* Size of the element in screen coordinates.
|
||||
*/
|
||||
size: DOMRect | null;
|
||||
/**
|
||||
* From value in the timeline keyframes collection.
|
||||
*/
|
||||
min: number;
|
||||
/**
|
||||
* To value in the timeline keyframes collection.
|
||||
*/
|
||||
max: number;
|
||||
|
||||
/**
|
||||
* Related Group Model.
|
||||
*/
|
||||
groupModel: string | TimelineGroup;
|
||||
|
||||
/**
|
||||
* Related Grouped keyframes view models.
|
||||
*/
|
||||
keyframesViewModels: TimelineKeyframeViewModel[];
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { TimelineKeyframeShape } from '../enums/timelineKeyframeShape';
|
||||
import { TimelineKeyframe } from '../models/timelineKeyframe';
|
||||
import { TimelineGroupViewModel } from './timelineGroupViewModel';
|
||||
import { TimelineRowViewModel } from './timelineRowViewModel';
|
||||
|
||||
/**
|
||||
* Timeline calculated view model.
|
||||
*/
|
||||
export interface TimelineKeyframeViewModel {
|
||||
/**
|
||||
* Size of the keyframe.
|
||||
*/
|
||||
size: DOMRect;
|
||||
shape: TimelineKeyframeShape;
|
||||
/**
|
||||
* Related keyframe model.
|
||||
*/
|
||||
model: TimelineKeyframe;
|
||||
/**
|
||||
* Related calculated parent row view model.
|
||||
*/
|
||||
rowViewModel: TimelineRowViewModel;
|
||||
/**
|
||||
* Related calculated parent group view model.
|
||||
*/
|
||||
groupViewModel: TimelineGroupViewModel;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { TimelineRow } from '../models/timelineRow';
|
||||
import { TimelineGroupViewModel } from './timelineGroupViewModel';
|
||||
import { TimelineKeyframeViewModel } from './timelineKeyframeViewModel';
|
||||
|
||||
export interface TimelineRowViewModel {
|
||||
/**
|
||||
* Screen coordinates of the element.
|
||||
*/
|
||||
size: DOMRect;
|
||||
/**
|
||||
* Related row model.
|
||||
*/
|
||||
model: TimelineRow;
|
||||
/**
|
||||
* Current row index.
|
||||
*/
|
||||
index: number;
|
||||
/**
|
||||
* Row margin bottom
|
||||
*/
|
||||
marginBottom: number;
|
||||
/**
|
||||
* Collection of the keyframes groups view models exists in the current row.
|
||||
*/
|
||||
groupsViewModels: TimelineGroupViewModel[];
|
||||
|
||||
/**
|
||||
* All keyframes in the current row.
|
||||
*/
|
||||
keyframesViewModels: TimelineKeyframeViewModel[];
|
||||
min: number | null;
|
||||
max: number | null;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { TimelineKeyframeViewModel } from './timelineKeyframeViewModel';
|
||||
import { TimelineRowViewModel } from './timelineRowViewModel';
|
||||
|
||||
export interface TimelineViewModel {
|
||||
/**
|
||||
* Screen coordinates of the element.
|
||||
*/
|
||||
size: DOMRect;
|
||||
/**
|
||||
* Keyframes view models.
|
||||
*/
|
||||
keyframesViewModels: TimelineKeyframeViewModel[];
|
||||
/**
|
||||
* Collection of the rows sizes.
|
||||
*/
|
||||
rowsViewModels: TimelineRowViewModel[];
|
||||
|
||||
min: number | null;
|
||||
max: number | null;
|
||||
}
|
||||
Reference in New Issue
Block a user