Merge pull request #2827 from freesewing/multiset
feat(core): Adds support for managing multiple sets of settings (aka mutisets)
This commit is contained in:
commit
026560ef24
47 changed files with 5034 additions and 3428 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -96,6 +96,7 @@ node_modules
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
export
|
export
|
||||||
|
packages/core/out
|
||||||
|
|
||||||
# Prebuild files
|
# Prebuild files
|
||||||
prebuild/*.json
|
prebuild/*.json
|
||||||
|
|
|
@ -23,6 +23,7 @@ core:
|
||||||
testci: 'mocha tests/*.test.mjs'
|
testci: 'mocha tests/*.test.mjs'
|
||||||
prettier: "npx prettier --write 'src/*.mjs' 'tests/*.mjs'"
|
prettier: "npx prettier --write 'src/*.mjs' 'tests/*.mjs'"
|
||||||
lint: "npx eslint 'src/*.mjs' 'tests/*.mjs'"
|
lint: "npx eslint 'src/*.mjs' 'tests/*.mjs'"
|
||||||
|
jsdoc: "jsdoc -c jsdoc.json -r src"
|
||||||
i18n:
|
i18n:
|
||||||
prebuild: 'node scripts/prebuilder.mjs'
|
prebuild: 'node scripts/prebuilder.mjs'
|
||||||
models:
|
models:
|
||||||
|
|
1
packages/core/.eslintignore
Normal file
1
packages/core/.eslintignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
jsdoc.json
|
17
packages/core/jsdoc.json
Normal file
17
packages/core/jsdoc.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"plugins": [],
|
||||||
|
"recurseDepth": 2,
|
||||||
|
"source": {
|
||||||
|
"includePattern": ".+\\.mjs?$",
|
||||||
|
"excludePattern": "(^|\\/|\\\\)_"
|
||||||
|
},
|
||||||
|
"sourceType": "module",
|
||||||
|
"tags": {
|
||||||
|
"allowUnknownTags": true,
|
||||||
|
"dictionaries": ["jsdoc","closure"]
|
||||||
|
},
|
||||||
|
"templates": {
|
||||||
|
"cleverLinks": false,
|
||||||
|
"monospaceLinks": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,7 @@
|
||||||
"report": "c8 report",
|
"report": "c8 report",
|
||||||
"testci": "mocha tests/*.test.mjs",
|
"testci": "mocha tests/*.test.mjs",
|
||||||
"prettier": "npx prettier --write 'src/*.mjs' 'tests/*.mjs'",
|
"prettier": "npx prettier --write 'src/*.mjs' 'tests/*.mjs'",
|
||||||
|
"jsdoc": "jsdoc -c jsdoc.json -r src",
|
||||||
"cibuild_step0": "node build.mjs"
|
"cibuild_step0": "node build.mjs"
|
||||||
},
|
},
|
||||||
"peerDependencies": {},
|
"peerDependencies": {},
|
||||||
|
|
|
@ -1,8 +1,30 @@
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// CONSTRUCTOR //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for Attributes
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @return {Attributes} this - The Attributes instance
|
||||||
|
*/
|
||||||
export function Attributes() {
|
export function Attributes() {
|
||||||
this.list = {}
|
this.list = {}
|
||||||
|
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds an attribute */
|
//////////////////////////////////////////////
|
||||||
|
// PUBLIC METHODS //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an attribute
|
||||||
|
*
|
||||||
|
* @param {string} name - Name of the attribute to add
|
||||||
|
* @param {string} value - Value of the attribute to add
|
||||||
|
* @return {Attributes} this - The Attributes instance
|
||||||
|
*/
|
||||||
Attributes.prototype.add = function (name, value) {
|
Attributes.prototype.add = function (name, value) {
|
||||||
if (typeof this.list[name] === 'undefined') {
|
if (typeof this.list[name] === 'undefined') {
|
||||||
this.list[name] = []
|
this.list[name] = []
|
||||||
|
@ -12,75 +34,12 @@ Attributes.prototype.add = function (name, value) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets an attribute, overwriting existing value */
|
/**
|
||||||
Attributes.prototype.set = function (name, value) {
|
* Return a props object for attributes with a fiven prefix (typically used for data-text)
|
||||||
this.list[name] = [value]
|
*
|
||||||
|
* @param {string} prefix - The prefix to filter attributes on
|
||||||
return this
|
* @return {object} props - The attributes as props
|
||||||
}
|
*/
|
||||||
|
|
||||||
/** Sets an attribute, but only if it's not currently set */
|
|
||||||
Attributes.prototype.setIfUnset = function (name, value) {
|
|
||||||
if (typeof this.list[name] === 'undefined') this.list[name] = [value]
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Removes an attribute */
|
|
||||||
Attributes.prototype.remove = function (name) {
|
|
||||||
delete this.list[name]
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Retrieves an attribute */
|
|
||||||
Attributes.prototype.get = function (name) {
|
|
||||||
if (typeof this.list[name] === 'undefined') return false
|
|
||||||
else return this.list[name].join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Retrieves an attribute as array*/
|
|
||||||
Attributes.prototype.getAsArray = function (name) {
|
|
||||||
if (typeof this.list[name] === 'undefined') return false
|
|
||||||
else return this.list[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns SVG code for attributes */
|
|
||||||
Attributes.prototype.render = function () {
|
|
||||||
let svg = ''
|
|
||||||
for (let key in this.list) {
|
|
||||||
svg += ` ${key}="${this.list[key].join(' ')}"`
|
|
||||||
}
|
|
||||||
|
|
||||||
return svg
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns CSS code for attributes */
|
|
||||||
Attributes.prototype.renderAsCss = function () {
|
|
||||||
let css = ''
|
|
||||||
for (let key in this.list) {
|
|
||||||
css += ` ${key}:${this.list[key].join(' ')};`
|
|
||||||
}
|
|
||||||
|
|
||||||
return css
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns SVG code for attributes with a fiven prefix
|
|
||||||
* typically used for data-text*/
|
|
||||||
Attributes.prototype.renderIfPrefixIs = function (prefix = '') {
|
|
||||||
let svg = ''
|
|
||||||
let prefixLen = prefix.length
|
|
||||||
for (let key in this.list) {
|
|
||||||
if (key.substr(0, prefixLen) === prefix) {
|
|
||||||
svg += ` ${key.substr(prefixLen)}="${this.list[key].join(' ')}"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return svg
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a props object for attributes with a fiven prefix
|
|
||||||
* typically used for data-text*/
|
|
||||||
Attributes.prototype.asPropsIfPrefixIs = function (prefix = '') {
|
Attributes.prototype.asPropsIfPrefixIs = function (prefix = '') {
|
||||||
let props = {}
|
let props = {}
|
||||||
let prefixLen = prefix.length
|
let prefixLen = prefix.length
|
||||||
|
@ -95,10 +54,120 @@ Attributes.prototype.asPropsIfPrefixIs = function (prefix = '') {
|
||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a deep copy of this */
|
/**
|
||||||
|
* Return a deep copy of this
|
||||||
|
*
|
||||||
|
* @return {object} this - The Attributes instance
|
||||||
|
*/
|
||||||
Attributes.prototype.clone = function () {
|
Attributes.prototype.clone = function () {
|
||||||
let clone = new Attributes()
|
let clone = new Attributes()
|
||||||
clone.list = JSON.parse(JSON.stringify(this.list))
|
clone.list = JSON.parse(JSON.stringify(this.list))
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an attribute
|
||||||
|
*
|
||||||
|
* @param {string} name - Name of the attribute to get
|
||||||
|
* @return value - The value under name
|
||||||
|
*/
|
||||||
|
Attributes.prototype.get = function (name) {
|
||||||
|
if (typeof this.list[name] === 'undefined') return false
|
||||||
|
else return this.list[name].join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an attribute as array
|
||||||
|
*
|
||||||
|
* @param {string} name - Name of the attribute to set
|
||||||
|
* @return {object} this - The Attributes instance
|
||||||
|
*/
|
||||||
|
Attributes.prototype.getAsArray = function (name) {
|
||||||
|
if (typeof this.list[name] === 'undefined') return false
|
||||||
|
else return this.list[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an attribute
|
||||||
|
*
|
||||||
|
* @param {string} name - Name of the attribute to set
|
||||||
|
* @return {object} this - The Attributes instance
|
||||||
|
*/
|
||||||
|
Attributes.prototype.remove = function (name) {
|
||||||
|
delete this.list[name]
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return SVG code for attributes
|
||||||
|
*
|
||||||
|
* @return {string} svg - The SVG code
|
||||||
|
*/
|
||||||
|
Attributes.prototype.render = function () {
|
||||||
|
let svg = ''
|
||||||
|
for (let key in this.list) {
|
||||||
|
svg += ` ${key}="${this.list[key].join(' ')}"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return svg
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return CSS code for attributes
|
||||||
|
*
|
||||||
|
* @return {string} css - The CSS code
|
||||||
|
*/
|
||||||
|
Attributes.prototype.renderAsCss = function () {
|
||||||
|
let css = ''
|
||||||
|
for (let key in this.list) {
|
||||||
|
css += ` ${key}:${this.list[key].join(' ')};`
|
||||||
|
}
|
||||||
|
|
||||||
|
return css
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return SVG code for attributes with a fiven prefix (typically used for data-text)
|
||||||
|
*
|
||||||
|
* @param {string} prefix - The prefix to filter attributes on
|
||||||
|
* @return {string} svg - The SVG code
|
||||||
|
*/
|
||||||
|
Attributes.prototype.renderIfPrefixIs = function (prefix = '') {
|
||||||
|
let svg = ''
|
||||||
|
let prefixLen = prefix.length
|
||||||
|
for (let key in this.list) {
|
||||||
|
if (key.substr(0, prefixLen) === prefix) {
|
||||||
|
svg += ` ${key.substr(prefixLen)}="${this.list[key].join(' ')}"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return svg
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an attribute, overwriting existing value
|
||||||
|
*
|
||||||
|
* @param {string} name - Name of the attribute to set
|
||||||
|
* @param {string} value - Value of the attribute to set
|
||||||
|
* @return {Attributes} this - The Attributes instance
|
||||||
|
*/
|
||||||
|
Attributes.prototype.set = function (name, value) {
|
||||||
|
this.list[name] = [value]
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an attribute, but only if it's not currently set
|
||||||
|
*
|
||||||
|
* @param {string} name - Name of the attribute to set
|
||||||
|
* @param {string} value - Value of the attribute to set
|
||||||
|
* @return {Attributes} this - The Attributes instance
|
||||||
|
*/
|
||||||
|
Attributes.prototype.setIfUnset = function (name, value) {
|
||||||
|
if (typeof this.list[name] === 'undefined') this.list[name] = [value]
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
export const loadDesignDefaults = () => ({
|
/**
|
||||||
|
* Return an object holding the defaults for a design configuration
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @private
|
||||||
|
* @return {object} defaults - The default design configuration
|
||||||
|
*/
|
||||||
|
export const __loadDesignDefaults = () => ({
|
||||||
measurements: [],
|
measurements: [],
|
||||||
optionalMeasurements: [],
|
optionalMeasurements: [],
|
||||||
options: {},
|
options: {},
|
||||||
|
optionDistance: {},
|
||||||
parts: [],
|
parts: [],
|
||||||
data: {},
|
data: {},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
export const loadPatternDefaults = () => ({
|
/**
|
||||||
|
* Return an object holding the defaults for pattern settings
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @private
|
||||||
|
* @return {object} defaults - The default pattern settings
|
||||||
|
*/
|
||||||
|
export const __loadPatternDefaults = () => ({
|
||||||
complete: true,
|
complete: true,
|
||||||
idPrefix: 'fs-',
|
idPrefix: 'fs-',
|
||||||
|
stackPrefix: '',
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
units: 'metric',
|
units: 'metric',
|
||||||
margin: 2,
|
margin: 2,
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
import { Pattern } from './pattern.mjs'
|
import { Pattern } from './pattern.mjs'
|
||||||
import { loadDesignDefaults } from './config.mjs'
|
import { __loadDesignDefaults } from './config.mjs'
|
||||||
|
|
||||||
/*
|
//////////////////////////////////////////////
|
||||||
* The Design constructor. Returns a Pattern constructor
|
// CONSTRUCTOR //
|
||||||
* So it's sort of a super-constructor
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a Pattern constructor (it's a super-constructor)
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {object} config - The design configuration
|
||||||
|
* @return {function} pattern - The pattern constructor
|
||||||
*/
|
*/
|
||||||
export function Design(config) {
|
export function Design(config) {
|
||||||
// Initialize config with defaults
|
// Initialize config with defaults
|
||||||
config = { ...loadDesignDefaults(), ...config }
|
config = { ...__loadDesignDefaults(), ...config }
|
||||||
|
|
||||||
// Create the pattern constructor
|
// Create the pattern constructor
|
||||||
const pattern = function (settings) {
|
const pattern = function (...sets) {
|
||||||
// Pass the design config
|
// Pass the design config
|
||||||
Pattern.call(this, config)
|
Pattern.call(this, config)
|
||||||
|
|
||||||
// Pass the pattern settings
|
// Pass the pattern settings
|
||||||
return this.__applySettings(settings)
|
return this.__applySettings(sets)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up inheritance
|
// Set up inheritance
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Returns an object holding the defaults hooks structure
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @return {object} hooks - The default hooks holding structure
|
||||||
|
*/
|
||||||
export function Hooks() {
|
export function Hooks() {
|
||||||
return {
|
return {
|
||||||
preDraft: [],
|
preDraft: [],
|
||||||
|
|
|
@ -1,39 +1,37 @@
|
||||||
|
import { Bezier } from 'bezier-js'
|
||||||
import { Attributes } from './attributes.mjs'
|
import { Attributes } from './attributes.mjs'
|
||||||
import { Design } from './design.mjs'
|
import { Design } from './design.mjs'
|
||||||
import { Pattern } from './pattern.mjs'
|
import { Pattern } from './pattern.mjs'
|
||||||
|
import { Part } from './part.mjs'
|
||||||
import { Point } from './point.mjs'
|
import { Point } from './point.mjs'
|
||||||
import { Path } from './path.mjs'
|
import { Path } from './path.mjs'
|
||||||
import { Snippet } from './snippet.mjs'
|
import { Snippet } from './snippet.mjs'
|
||||||
import { Store } from './store.mjs'
|
import { Store } from './store.mjs'
|
||||||
import {
|
import {
|
||||||
isCoord,
|
beamIntersectsCircle,
|
||||||
capitalize,
|
|
||||||
beamsIntersect,
|
|
||||||
linesIntersect,
|
|
||||||
pointOnBeam,
|
|
||||||
pointOnLine,
|
|
||||||
pointOnCurve,
|
|
||||||
splitCurve,
|
|
||||||
beamIntersectsX,
|
beamIntersectsX,
|
||||||
beamIntersectsY,
|
beamIntersectsY,
|
||||||
units,
|
beamsIntersect,
|
||||||
lineIntersectsCurve,
|
capitalize,
|
||||||
|
circlesIntersect,
|
||||||
|
curveEdge,
|
||||||
curveIntersectsX,
|
curveIntersectsX,
|
||||||
curveIntersectsY,
|
curveIntersectsY,
|
||||||
curvesIntersect,
|
curvesIntersect,
|
||||||
circlesIntersect,
|
|
||||||
beamIntersectsCircle,
|
|
||||||
lineIntersectsCircle,
|
|
||||||
curveEdge,
|
|
||||||
stretchToScale,
|
|
||||||
round,
|
|
||||||
sampleStyle,
|
|
||||||
deg2rad,
|
deg2rad,
|
||||||
rad2deg,
|
|
||||||
pctBasedOn,
|
|
||||||
Bezier,
|
|
||||||
generateStackTransform,
|
generateStackTransform,
|
||||||
macroName,
|
lineIntersectsCircle,
|
||||||
|
lineIntersectsCurve,
|
||||||
|
linesIntersect,
|
||||||
|
pctBasedOn,
|
||||||
|
pointOnBeam,
|
||||||
|
pointOnCurve,
|
||||||
|
pointOnLine,
|
||||||
|
rad2deg,
|
||||||
|
round,
|
||||||
|
splitCurve,
|
||||||
|
stretchToScale,
|
||||||
|
units,
|
||||||
} from './utils.mjs'
|
} from './utils.mjs'
|
||||||
import { version } from '../data.mjs'
|
import { version } from '../data.mjs'
|
||||||
|
|
||||||
|
@ -44,35 +42,34 @@ export {
|
||||||
Pattern,
|
Pattern,
|
||||||
Point,
|
Point,
|
||||||
Path,
|
Path,
|
||||||
|
Part,
|
||||||
Snippet,
|
Snippet,
|
||||||
Store,
|
Store,
|
||||||
|
version,
|
||||||
Bezier,
|
Bezier,
|
||||||
capitalize,
|
// Utils
|
||||||
beamsIntersect,
|
beamIntersectsCircle,
|
||||||
linesIntersect,
|
|
||||||
pointOnBeam,
|
|
||||||
pointOnLine,
|
|
||||||
pointOnCurve,
|
|
||||||
splitCurve,
|
|
||||||
beamIntersectsX,
|
beamIntersectsX,
|
||||||
beamIntersectsY,
|
beamIntersectsY,
|
||||||
units,
|
beamsIntersect,
|
||||||
lineIntersectsCurve,
|
capitalize,
|
||||||
|
circlesIntersect,
|
||||||
|
curveEdge,
|
||||||
curveIntersectsX,
|
curveIntersectsX,
|
||||||
curveIntersectsY,
|
curveIntersectsY,
|
||||||
curvesIntersect,
|
curvesIntersect,
|
||||||
circlesIntersect,
|
|
||||||
beamIntersectsCircle,
|
|
||||||
lineIntersectsCircle,
|
|
||||||
curveEdge,
|
|
||||||
stretchToScale,
|
|
||||||
round,
|
|
||||||
sampleStyle,
|
|
||||||
deg2rad,
|
deg2rad,
|
||||||
rad2deg,
|
|
||||||
pctBasedOn,
|
|
||||||
generateStackTransform,
|
generateStackTransform,
|
||||||
macroName,
|
lineIntersectsCircle,
|
||||||
isCoord,
|
lineIntersectsCurve,
|
||||||
version,
|
linesIntersect,
|
||||||
|
pctBasedOn,
|
||||||
|
pointOnBeam,
|
||||||
|
pointOnCurve,
|
||||||
|
pointOnLine,
|
||||||
|
rad2deg,
|
||||||
|
round,
|
||||||
|
splitCurve,
|
||||||
|
stretchToScale,
|
||||||
|
units,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
export function Option(config) {
|
|
||||||
this.id = config.id
|
|
||||||
this.config = config
|
|
||||||
this.val = config.val
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
|
@ -1,23 +1,33 @@
|
||||||
import { Attributes } from './attributes.mjs'
|
import { Attributes } from './attributes.mjs'
|
||||||
import * as utils from './utils.mjs'
|
import * as utils from './utils.mjs'
|
||||||
import { Point } from './point.mjs'
|
import { Point, pointsProxy } from './point.mjs'
|
||||||
import { Path } from './path.mjs'
|
import { Path, pathsProxy } from './path.mjs'
|
||||||
import { Snippet } from './snippet.mjs'
|
import { Snippet, snippetsProxy } from './snippet.mjs'
|
||||||
import { Hooks } from './hooks.mjs'
|
import { Hooks } from './hooks.mjs'
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// CONSTRUCTOR //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a Part
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @return {Part} this - The Part instance
|
||||||
|
*/
|
||||||
export function Part() {
|
export function Part() {
|
||||||
// Non-enumerable properties
|
// Non-enumerable properties
|
||||||
utils.addNonEnumProp(this, 'freeId', 0)
|
utils.__addNonEnumProp(this, 'freeId', 0)
|
||||||
utils.addNonEnumProp(this, 'topLeft', false)
|
utils.__addNonEnumProp(this, 'topLeft', false)
|
||||||
utils.addNonEnumProp(this, 'bottomRight', false)
|
utils.__addNonEnumProp(this, 'bottomRight', false)
|
||||||
utils.addNonEnumProp(this, 'width', false)
|
utils.__addNonEnumProp(this, 'width', false)
|
||||||
utils.addNonEnumProp(this, 'height', false)
|
utils.__addNonEnumProp(this, 'height', false)
|
||||||
utils.addNonEnumProp(this, 'utils', utils)
|
utils.__addNonEnumProp(this, 'utils', utils)
|
||||||
utils.addNonEnumProp(this, 'layout', { move: { x: 0, y: 0 } })
|
utils.__addNonEnumProp(this, 'layout', { move: { x: 0, y: 0 } })
|
||||||
utils.addNonEnumProp(this, 'Point', Point)
|
utils.__addNonEnumProp(this, 'Point', Point)
|
||||||
utils.addNonEnumProp(this, 'Path', Path)
|
utils.__addNonEnumProp(this, 'Path', Path)
|
||||||
utils.addNonEnumProp(this, 'Snippet', Snippet)
|
utils.__addNonEnumProp(this, 'Snippet', Snippet)
|
||||||
utils.addNonEnumProp(this, 'hooks', new Hooks())
|
utils.__addNonEnumProp(this, 'hooks', new Hooks())
|
||||||
|
|
||||||
// Enumerable properties
|
// Enumerable properties
|
||||||
this.render = true // FIXME: Replace render with hide
|
this.render = true // FIXME: Replace render with hide
|
||||||
|
@ -31,56 +41,163 @@ export function Part() {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
Part.prototype.macroClosure = function () {
|
//////////////////////////////////////////////
|
||||||
let self = this
|
// PUBLIC METHODS //
|
||||||
let method = function (key, args) {
|
//////////////////////////////////////////////
|
||||||
let macro = utils.macroName(key)
|
|
||||||
if (typeof self[macro] === 'function') self[macro](args)
|
|
||||||
}
|
|
||||||
|
|
||||||
return method
|
/**
|
||||||
|
* Adds an attribute in a chainable way
|
||||||
|
*
|
||||||
|
* @param {string} name - Name of the attribute to add
|
||||||
|
* @param {string} value - Value of the attribute to add
|
||||||
|
* @param {bool} overwrite - Whether to overwrite an existing attrubute or not
|
||||||
|
* @return {Part} this - The part instance
|
||||||
|
*/
|
||||||
|
Part.prototype.attr = function (name, value, overwrite = false) {
|
||||||
|
if (overwrite) this.attributes.set(name, value)
|
||||||
|
else this.attributes.add(name, value)
|
||||||
|
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
Part.prototype.runHooks = function (hookName, data = false) {
|
/**
|
||||||
if (data === false) data = this
|
* Returns on unused ID (unused in this part)
|
||||||
let hooks = this.hooks[hookName]
|
*
|
||||||
if (hooks && hooks.length > 0) {
|
* @param {string} prefix - An optional prefix to apply to the ID
|
||||||
for (let hook of hooks) {
|
* @return {string} id - The id
|
||||||
hook.method(data, hook.data)
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns an unused ID */
|
|
||||||
Part.prototype.getId = function (prefix = '') {
|
Part.prototype.getId = function (prefix = '') {
|
||||||
this.freeId += 1
|
this.freeId += 1
|
||||||
|
|
||||||
return prefix + this.freeId
|
return prefix + this.freeId
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a value formatted for units provided in settings */
|
/** Returns an object with shorthand access for pattern design */
|
||||||
Part.prototype.unitsClosure = function () {
|
/**
|
||||||
const self = this
|
* Returns an object that will be passed to draft method to be destructured
|
||||||
const method = function (value) {
|
*
|
||||||
if (typeof value !== 'number')
|
* @return {object} short - The so-called shorthand object with what you might need in your draft method
|
||||||
self.context.store.log.warning(
|
*/
|
||||||
`Calling \`units(value)\` but \`value\` is not a number (\`${typeof value}\`)`
|
Part.prototype.shorthand = function () {
|
||||||
)
|
const complete = this.context.settings?.complete ? true : false
|
||||||
return utils.units(value, self.context.settings.units)
|
const paperless = this.context.settings?.paperless === true ? true : false
|
||||||
|
const sa = this.context.settings?.complete ? this.context.settings?.sa || 0 : 0
|
||||||
|
const shorthand = {
|
||||||
|
part: this,
|
||||||
|
sa,
|
||||||
|
scale: this.context.settings?.scale,
|
||||||
|
store: this.context.store,
|
||||||
|
macro: this.__macroClosure(),
|
||||||
|
units: this.__unitsClosure(),
|
||||||
|
utils: utils,
|
||||||
|
complete,
|
||||||
|
paperless,
|
||||||
|
events: this.context.events,
|
||||||
|
log: this.context.store.log,
|
||||||
|
addCut: this.addCut,
|
||||||
|
removeCut: this.removeCut,
|
||||||
|
}
|
||||||
|
// Add top-level store methods and add a part name parameter
|
||||||
|
const partName = this.name
|
||||||
|
for (const [key, method] of Object.entries(this.context.store)) {
|
||||||
|
if (typeof method === 'function')
|
||||||
|
shorthand[key] = function (...args) {
|
||||||
|
return method(partName, ...args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return method
|
// We'll need this
|
||||||
|
let self = this
|
||||||
|
|
||||||
|
// Wrap the Point constructor so objects can log
|
||||||
|
shorthand.Point = function (x, y) {
|
||||||
|
Point.apply(this, [x, y])
|
||||||
|
Object.defineProperty(this, 'log', { value: self.context.store.log })
|
||||||
|
}
|
||||||
|
shorthand.Point.prototype = Object.create(Point.prototype)
|
||||||
|
// Wrap the Path constructor so objects can log
|
||||||
|
shorthand.Path = function () {
|
||||||
|
Path.apply(this, [true])
|
||||||
|
Object.defineProperty(this, 'log', { value: self.context.store.log })
|
||||||
|
}
|
||||||
|
shorthand.Path.prototype = Object.create(Path.prototype)
|
||||||
|
// Wrap the Snippet constructor so objects can log
|
||||||
|
shorthand.Snippet = function (def, anchor) {
|
||||||
|
Snippet.apply(this, [def, anchor, true])
|
||||||
|
Snippet.apply(this, arguments)
|
||||||
|
Object.defineProperty(this, 'log', { value: self.context.store.log })
|
||||||
|
}
|
||||||
|
shorthand.Snippet.prototype = Object.create(Snippet.prototype)
|
||||||
|
|
||||||
|
// Proxy points, paths, snippets, measurements, options, and absoluteOptions
|
||||||
|
shorthand.points = new Proxy(this.points || {}, pointsProxy(self.points, self.context.store.log))
|
||||||
|
shorthand.paths = new Proxy(this.paths || {}, pathsProxy(self.paths, self.context.store.log))
|
||||||
|
shorthand.snippets = new Proxy(
|
||||||
|
this.snippets || {},
|
||||||
|
snippetsProxy(self.snippets, self.context.store.log)
|
||||||
|
)
|
||||||
|
shorthand.measurements = new Proxy(this.context.settings.measurements || {}, {
|
||||||
|
get: function (measurements, name) {
|
||||||
|
if (typeof measurements[name] === 'undefined')
|
||||||
|
self.context.store.log.warning(
|
||||||
|
`Tried to access \`measurements.${name}\` but it is \`undefined\``
|
||||||
|
)
|
||||||
|
return Reflect.get(...arguments)
|
||||||
|
},
|
||||||
|
set: (measurements, name, value) => (self.context.settings.measurements[name] = value),
|
||||||
|
})
|
||||||
|
shorthand.options = new Proxy(this.context.settings.options || {}, {
|
||||||
|
get: function (options, name) {
|
||||||
|
if (typeof options[name] === 'undefined')
|
||||||
|
self.context.store.log.warning(
|
||||||
|
`Tried to access \`options.${name}\` but it is \`undefined\``
|
||||||
|
)
|
||||||
|
return Reflect.get(...arguments)
|
||||||
|
},
|
||||||
|
set: (options, name, value) => (self.context.settings.options[name] = value),
|
||||||
|
})
|
||||||
|
shorthand.absoluteOptions = new Proxy(this.context.settings.absoluteOptions || {}, {
|
||||||
|
get: function (absoluteOptions, name) {
|
||||||
|
if (typeof absoluteOptions[name] === 'undefined')
|
||||||
|
self.context.store.log.warning(
|
||||||
|
`Tried to access \`absoluteOptions.${name}\` but it is \`undefined\``
|
||||||
|
)
|
||||||
|
return Reflect.get(...arguments)
|
||||||
|
},
|
||||||
|
set: (absoluteOptions, name, value) => (self.context.settings.absoluteOptions[name] = value),
|
||||||
|
})
|
||||||
|
|
||||||
|
return shorthand
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Calculates the part's bounding box and sets it */
|
/**
|
||||||
Part.prototype.boundary = function () {
|
* Returns a value formatted for units set in settings
|
||||||
|
*
|
||||||
|
* @param {float} input - The value to format
|
||||||
|
* @return {string} result - The input formatted for the units set in settings
|
||||||
|
*/
|
||||||
|
Part.prototype.units = function (input) {
|
||||||
|
return utils.units(input, this.context.settings.units)
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// PRIVATE METHODS //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the part's bounding box and mutates the part to set it
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {Part} this - The part instance
|
||||||
|
*/
|
||||||
|
Part.prototype.__boundary = function () {
|
||||||
if (this.topLeft) return this // Cached
|
if (this.topLeft) return this // Cached
|
||||||
|
|
||||||
let topLeft = new Point(Infinity, Infinity)
|
let topLeft = new Point(Infinity, Infinity)
|
||||||
let bottomRight = new Point(-Infinity, -Infinity)
|
let bottomRight = new Point(-Infinity, -Infinity)
|
||||||
for (let key in this.paths) {
|
for (let key in this.paths) {
|
||||||
try {
|
try {
|
||||||
let path = this.paths[key].boundary()
|
let path = this.paths[key].__boundary()
|
||||||
if (path.render) {
|
if (path.render) {
|
||||||
if (path.topLeft.x < topLeft.x) topLeft.x = path.topLeft.x
|
if (path.topLeft.x < topLeft.x) topLeft.x = path.topLeft.x
|
||||||
if (path.topLeft.y < topLeft.y) topLeft.y = path.topLeft.y
|
if (path.topLeft.y < topLeft.y) topLeft.y = path.topLeft.y
|
||||||
|
@ -120,16 +237,14 @@ Part.prototype.boundary = function () {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds an attribute. This is here to make this call chainable in assignment */
|
/**
|
||||||
Part.prototype.attr = function (name, value, overwrite = false) {
|
* Copies point/path/snippet data from part orig into this
|
||||||
if (overwrite) this.attributes.set(name, value)
|
*
|
||||||
else this.attributes.add(name, value)
|
* @private
|
||||||
|
* @param {object} orig - The original part to inject into this
|
||||||
return this
|
* @return {Part} this - The part instance
|
||||||
}
|
*/
|
||||||
|
Part.prototype.__inject = function (orig) {
|
||||||
/** Copies point/path/snippet data from part orig into this */
|
|
||||||
Part.prototype.inject = function (orig) {
|
|
||||||
const findBasePoint = (p) => {
|
const findBasePoint = (p) => {
|
||||||
for (let i in orig.points) {
|
for (let i in orig.points) {
|
||||||
if (orig.points[i] === p) return i
|
if (orig.points[i] === p) return i
|
||||||
|
@ -161,196 +276,37 @@ Part.prototype.inject = function (orig) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
Part.prototype.units = function (input) {
|
/**
|
||||||
return utils.units(input, this.context.settings.units)
|
* Returns a closure holding the macro method
|
||||||
}
|
*
|
||||||
|
* @private
|
||||||
/** Returns an object with shorthand access for pattern design */
|
* @return {function} method - The closured macro method
|
||||||
Part.prototype.shorthand = function () {
|
*/
|
||||||
const complete = this.context.settings?.complete ? true : false
|
Part.prototype.__macroClosure = function () {
|
||||||
const paperless = this.context.settings?.paperless === true ? true : false
|
|
||||||
const sa = this.context.settings?.complete ? this.context.settings?.sa || 0 : 0
|
|
||||||
const shorthand = {
|
|
||||||
part: this,
|
|
||||||
sa,
|
|
||||||
scale: this.context.settings?.scale,
|
|
||||||
store: this.context.store,
|
|
||||||
macro: this.macroClosure(),
|
|
||||||
units: this.unitsClosure(),
|
|
||||||
utils: utils,
|
|
||||||
complete,
|
|
||||||
paperless,
|
|
||||||
events: this.context.events,
|
|
||||||
log: this.context.store.log,
|
|
||||||
addCut: this.addCut,
|
|
||||||
removeCut: this.removeCut,
|
|
||||||
}
|
|
||||||
// Add top-level store methods and add a part name parameter
|
|
||||||
const partName = this.name
|
|
||||||
for (const [key, method] of Object.entries(this.context.store)) {
|
|
||||||
if (typeof method === 'function')
|
|
||||||
shorthand[key] = function (...args) {
|
|
||||||
return method(partName, ...args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll need this
|
|
||||||
let self = this
|
let self = this
|
||||||
|
let method = function (key, args) {
|
||||||
|
let macro = utils.__macroName(key)
|
||||||
|
if (typeof self[macro] === 'function') self[macro](args)
|
||||||
|
}
|
||||||
|
|
||||||
// Wrap the Point constructor so objects can log
|
return method
|
||||||
shorthand.Point = function (x, y) {
|
|
||||||
Point.apply(this, [x, y, true])
|
|
||||||
Object.defineProperty(this, 'log', { value: self.context.store.log })
|
|
||||||
}
|
|
||||||
shorthand.Point.prototype = Object.create(Point.prototype)
|
|
||||||
// Wrap the Path constructor so objects can log
|
|
||||||
shorthand.Path = function () {
|
|
||||||
Path.apply(this, [true])
|
|
||||||
Object.defineProperty(this, 'log', { value: self.context.store.log })
|
|
||||||
}
|
|
||||||
shorthand.Path.prototype = Object.create(Path.prototype)
|
|
||||||
// Wrap the Snippet constructor so objects can log
|
|
||||||
shorthand.Snippet = function (def, anchor) {
|
|
||||||
Snippet.apply(this, [def, anchor, true])
|
|
||||||
Snippet.apply(this, arguments)
|
|
||||||
Object.defineProperty(this, 'log', { value: self.context.store.log })
|
|
||||||
}
|
|
||||||
shorthand.Snippet.prototype = Object.create(Snippet.prototype)
|
|
||||||
|
|
||||||
// Proxy the points object
|
|
||||||
const pointsProxy = {
|
|
||||||
get: function () {
|
|
||||||
return Reflect.get(...arguments)
|
|
||||||
},
|
|
||||||
set: (points, name, value) => {
|
|
||||||
// Constructor checks
|
|
||||||
if (value instanceof Point !== true)
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`\`points.${name}\` was set with a value that is not a \`Point\` object`
|
|
||||||
)
|
|
||||||
if (value.x == null || !utils.isCoord(value.x))
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`\`points.${name}\` was set with a \`x\` parameter that is not a \`number\``
|
|
||||||
)
|
|
||||||
if (value.y == null || !utils.isCoord(value.y))
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`\`points.${name}\` was set with a \`y\` parameter that is not a \`number\``
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
value.name = name
|
|
||||||
} catch (err) {
|
|
||||||
self.context.store.log.warning(`Could not set \`name\` property on \`points.${name}\``)
|
|
||||||
}
|
|
||||||
return (self.points[name] = value)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
shorthand.points = new Proxy(this.points || {}, pointsProxy)
|
|
||||||
// Proxy the paths object
|
|
||||||
const pathsProxy = {
|
|
||||||
get: function () {
|
|
||||||
return Reflect.get(...arguments)
|
|
||||||
},
|
|
||||||
set: (paths, name, value) => {
|
|
||||||
// Constructor checks
|
|
||||||
if (value instanceof Path !== true)
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`\`paths.${name}\` was set with a value that is not a \`Path\` object`
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
value.name = name
|
|
||||||
} catch (err) {
|
|
||||||
self.context.store.log.warning(`Could not set \`name\` property on \`paths.${name}\``)
|
|
||||||
}
|
|
||||||
return (self.paths[name] = value)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
shorthand.paths = new Proxy(this.paths || {}, pathsProxy)
|
|
||||||
// Proxy the snippets object
|
|
||||||
const snippetsProxy = {
|
|
||||||
get: function (...args) {
|
|
||||||
return Reflect.get(...args)
|
|
||||||
},
|
|
||||||
set: (snippets, name, value) => {
|
|
||||||
// Constructor checks
|
|
||||||
if (value instanceof Snippet !== true)
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`\`snippets.${name}\` was set with a value that is not a \`Snippet\` object`
|
|
||||||
)
|
|
||||||
if (typeof value.def !== 'string')
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`\`snippets.${name}\` was set with a \`def\` parameter that is not a \`string\``
|
|
||||||
)
|
|
||||||
if (value.anchor instanceof Point !== true)
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`\`snippets.${name}\` was set with an \`anchor\` parameter that is not a \`Point\``
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
value.name = name
|
|
||||||
} catch (err) {
|
|
||||||
self.context.store.log.warning(`Could not set \`name\` property on \`snippets.${name}\``)
|
|
||||||
}
|
|
||||||
return (self.snippets[name] = value)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
shorthand.snippets = new Proxy(this.snippets || {}, snippetsProxy)
|
|
||||||
// Proxy the measurements object
|
|
||||||
const measurementsProxy = {
|
|
||||||
get: function (measurements, name) {
|
|
||||||
if (typeof measurements[name] === 'undefined')
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`Tried to access \`measurements.${name}\` but it is \`undefined\``
|
|
||||||
)
|
|
||||||
return Reflect.get(...arguments)
|
|
||||||
},
|
|
||||||
set: (measurements, name, value) => (self.context.settings.measurements[name] = value),
|
|
||||||
}
|
|
||||||
shorthand.measurements = new Proxy(this.context.settings.measurements || {}, measurementsProxy)
|
|
||||||
// Proxy the options object
|
|
||||||
const optionsProxy = {
|
|
||||||
get: function (options, name) {
|
|
||||||
if (typeof options[name] === 'undefined')
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`Tried to access \`options.${name}\` but it is \`undefined\``
|
|
||||||
)
|
|
||||||
return Reflect.get(...arguments)
|
|
||||||
},
|
|
||||||
set: (options, name, value) => (self.context.settings.options[name] = value),
|
|
||||||
}
|
|
||||||
shorthand.options = new Proxy(this.context.settings.options || {}, optionsProxy)
|
|
||||||
// Proxy the absoluteOptions object
|
|
||||||
const absoluteOptionsProxy = {
|
|
||||||
get: function (absoluteOptions, name) {
|
|
||||||
if (typeof absoluteOptions[name] === 'undefined')
|
|
||||||
self.context.store.log.warning(
|
|
||||||
`Tried to access \`absoluteOptions.${name}\` but it is \`undefined\``
|
|
||||||
)
|
|
||||||
return Reflect.get(...arguments)
|
|
||||||
},
|
|
||||||
set: (absoluteOptions, name, value) => (self.context.settings.absoluteOptions[name] = value),
|
|
||||||
}
|
|
||||||
shorthand.absoluteOptions = new Proxy(
|
|
||||||
this.context.settings.absoluteOptions || {},
|
|
||||||
absoluteOptionsProxy
|
|
||||||
)
|
|
||||||
|
|
||||||
return shorthand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Part.prototype.isEmpty = function () {
|
/**
|
||||||
if (Object.keys(this.snippets).length > 0) return false
|
* Returns a method to format values in the units provided in settings
|
||||||
|
*
|
||||||
if (Object.keys(this.paths).length > 0) {
|
* @private
|
||||||
for (const p in this.paths) {
|
* @return {function} method - The closured units method
|
||||||
if (this.paths[p].render && this.paths[p].length()) return false
|
*/
|
||||||
}
|
Part.prototype.__unitsClosure = function () {
|
||||||
|
const self = this
|
||||||
|
const method = function (value) {
|
||||||
|
if (typeof value !== 'number')
|
||||||
|
self.context.store.log.warning(
|
||||||
|
`Calling \`units(value)\` but \`value\` is not a number (\`${typeof value}\`)`
|
||||||
|
)
|
||||||
|
return utils.units(value, self.context.settings.units)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const p in this.points) {
|
return method
|
||||||
if (this.points[p].attributes.get('data-text')) return false
|
|
||||||
if (this.points[p].attributes.get('data-circle')) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Part
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,230 +1,319 @@
|
||||||
import { Attributes } from './attributes.mjs'
|
import { Attributes } from './attributes.mjs'
|
||||||
|
import { __isCoord, rad2deg, deg2rad } from './utils.mjs'
|
||||||
|
|
||||||
export function Point(x, y, debug = false) {
|
//////////////////////////////////////////////
|
||||||
|
// CONSTRUCTOR //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a Point
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {float} x - X-coordinate of the Point
|
||||||
|
* @param {float} y - Y-coordinate of the Point
|
||||||
|
* @return {Point} this - The Point instance
|
||||||
|
*/
|
||||||
|
export function Point(x, y) {
|
||||||
this.x = x
|
this.x = x
|
||||||
this.y = y
|
this.y = y
|
||||||
this.attributes = new Attributes()
|
this.attributes = new Attributes()
|
||||||
Object.defineProperty(this, 'debug', { value: debug, configurable: true })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds the raise method for a path not created through the proxy **/
|
//////////////////////////////////////////////
|
||||||
Point.prototype.withRaise = function (raise = false) {
|
// PUBLIC METHODS //
|
||||||
if (raise) Object.defineProperty(this, 'raise', { value: raise })
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
return this
|
/**
|
||||||
|
* Returns the angle between this Point and that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point instance to calculate the angle with
|
||||||
|
* @return {float} angle - The angle between this Point and that Point
|
||||||
|
*/
|
||||||
|
Point.prototype.angle = function (that) {
|
||||||
|
let rad = Math.atan2(-1 * this.__check().dy(that.__check()), this.dx(that))
|
||||||
|
while (rad < 0) rad += 2 * Math.PI
|
||||||
|
|
||||||
|
return rad2deg(rad)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Debug method to validate point data **/
|
/**
|
||||||
Point.prototype.check = function () {
|
* Chainable way to add an attribute to the Point
|
||||||
if (typeof this.x !== 'number') this.raise.warning('X value of `Point` is not a number')
|
*
|
||||||
if (typeof this.y !== 'number') this.raise.warning('Y value of `Point` is not a number')
|
* @param {string} name - Name of the attribute to add
|
||||||
}
|
* @param {string} value - Value of the attribute to add
|
||||||
|
* @param {bool} overwrite - Whether to overwrite an existing attrubute or not
|
||||||
/** Radians to degrees */
|
* @return {object} this - The Point instance
|
||||||
Point.prototype.rad2deg = function (radians) {
|
*/
|
||||||
return radians * 57.29577951308232
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Degrees to radians */
|
|
||||||
Point.prototype.deg2rad = function (degrees) {
|
|
||||||
return degrees / 57.29577951308232
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Adds an attribute. This is here to make this call chainable in assignment */
|
|
||||||
Point.prototype.attr = function (name, value, overwrite = false) {
|
Point.prototype.attr = function (name, value, overwrite = false) {
|
||||||
this.check()
|
|
||||||
if (overwrite) this.attributes.set(name, value)
|
if (overwrite) this.attributes.set(name, value)
|
||||||
else this.attributes.add(name, value)
|
else this.attributes.add(name, value)
|
||||||
|
|
||||||
return this
|
return this.__check()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the distance between this point and that point */
|
/**
|
||||||
Point.prototype.dist = function (that) {
|
* returns an deel clone of this Point (including coordinates)
|
||||||
this.check()
|
*
|
||||||
that.check()
|
* @return {Point} clone - The cloned Point instance
|
||||||
let dx = this.x - that.x
|
*/
|
||||||
let dy = this.y - that.y
|
|
||||||
|
|
||||||
return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns slope of a line made by this point and that point */
|
|
||||||
Point.prototype.slope = function (that) {
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
return (that.y - this.y) / (that.x - this.x)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the x-delta between this point and that point */
|
|
||||||
Point.prototype.dx = function (that) {
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
|
|
||||||
return that.x - this.x
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the y-delta between this point and that point */
|
|
||||||
Point.prototype.dy = function (that) {
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
|
|
||||||
return that.y - this.y
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the angle between this point and that point */
|
|
||||||
Point.prototype.angle = function (that) {
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
|
|
||||||
let rad = Math.atan2(-1 * this.dy(that), this.dx(that))
|
|
||||||
while (rad < 0) rad += 2 * Math.PI
|
|
||||||
|
|
||||||
return this.rad2deg(rad)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Rotate this point deg around that point */
|
|
||||||
Point.prototype.rotate = function (deg, that) {
|
|
||||||
if (typeof deg !== 'number')
|
|
||||||
this.raise.warning('Called `Point.rotate(deg,that)` but `deg` is not a number')
|
|
||||||
if (that instanceof Point !== true)
|
|
||||||
this.raise.warning('Called `Point.rotate(deg,that)` but `that` is not a `Point` object')
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
let radius = this.dist(that)
|
|
||||||
let angle = this.angle(that)
|
|
||||||
let x = that.x + radius * Math.cos(this.deg2rad(angle + deg)) * -1
|
|
||||||
let y = that.y + radius * Math.sin(this.deg2rad(angle + deg))
|
|
||||||
|
|
||||||
return new Point(x, y, this.debug).withRaise(this.raise)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** returns an identical copy of this point */
|
|
||||||
Point.prototype.copy = function () {
|
|
||||||
this.check()
|
|
||||||
|
|
||||||
return new Point(this.x, this.y, this.debug).withRaise(this.raise)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Mirrors this point around X value of that point */
|
|
||||||
Point.prototype.flipX = function (that = false) {
|
|
||||||
this.check()
|
|
||||||
if (that) {
|
|
||||||
if (that instanceof Point !== true)
|
|
||||||
this.raise.warning('Called `Point.rotate(deg,that)` but `that` is not a `Point` object')
|
|
||||||
that.check()
|
|
||||||
}
|
|
||||||
if (that === false || that.x === 0)
|
|
||||||
return new Point(this.x * -1, this.y, this.debug).withRaise(this.raise)
|
|
||||||
else return new Point(that.x + this.dx(that), this.y, this.debug).withRaise(this.raise)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Mirrors this point around Y value of that point */
|
|
||||||
Point.prototype.flipY = function (that = false) {
|
|
||||||
this.check()
|
|
||||||
if (that) {
|
|
||||||
if (that instanceof Point !== true)
|
|
||||||
this.raise.warning('Called `Point.flipY(that)` but `that` is not a `Point` object')
|
|
||||||
that.check()
|
|
||||||
}
|
|
||||||
if (that === false || that.y === 0)
|
|
||||||
return new Point(this.x, this.y * -1, this.debug).withRaise(this.raise)
|
|
||||||
else return new Point(this.x, that.y + this.dy(that), this.debug).withRaise(this.raise)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Shifts this point distance in the deg direction */
|
|
||||||
Point.prototype.shift = function (deg, distance) {
|
|
||||||
this.check()
|
|
||||||
if (typeof deg !== 'number') this.raise.warning('Called `Point.shift` but `deg` is not a number')
|
|
||||||
if (typeof distance !== 'number')
|
|
||||||
this.raise.warning('Called `Point.shift` but `distance` is not a number')
|
|
||||||
let p = this.copy()
|
|
||||||
p.x += distance
|
|
||||||
|
|
||||||
return p.rotate(deg, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Shifts this point distance in the direction of that point */
|
|
||||||
Point.prototype.shiftTowards = function (that, distance) {
|
|
||||||
if (typeof distance !== 'number')
|
|
||||||
this.raise.warning('Called `Point.shiftTowards` but `distance` is not a number')
|
|
||||||
if (that instanceof Point !== true)
|
|
||||||
this.raise.warning(
|
|
||||||
'Called `Point.shiftTowards(that, distance)` but `that` is not a `Point` object'
|
|
||||||
)
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
|
|
||||||
return this.shift(this.angle(that), distance)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Checks whether this has the same coordinates as that */
|
|
||||||
Point.prototype.sitsOn = function (that) {
|
|
||||||
if (that instanceof Point !== true)
|
|
||||||
this.raise.warning('Called `Point.sitsOn(that)` but `that` is not a `Point` object')
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
if (this.x === that.x && this.y === that.y) return true
|
|
||||||
else return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Checks whether this has roughly the same coordinates as that */
|
|
||||||
Point.prototype.sitsRoughlyOn = function (that) {
|
|
||||||
if (that instanceof Point !== true)
|
|
||||||
this.raise.warning('Called `Point.sitsRoughlyOn(that)` but `that` is not a `Point` object')
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
if (Math.round(this.x) === Math.round(that.x) && Math.round(this.y) === Math.round(that.y))
|
|
||||||
return true
|
|
||||||
else return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Shifts this point fraction of the distance towards that point */
|
|
||||||
Point.prototype.shiftFractionTowards = function (that, fraction) {
|
|
||||||
if (that instanceof Point !== true)
|
|
||||||
this.raise.warning(
|
|
||||||
'Called `Point.shiftFractionTowards(that, fraction)` but `that` is not a `Point` object'
|
|
||||||
)
|
|
||||||
if (typeof fraction !== 'number')
|
|
||||||
this.raise.warning('Called `Point.shiftFractionTowards` but `fraction` is not a number')
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
|
|
||||||
return this.shiftTowards(that, this.dist(that) * fraction)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Shifts this point distance beyond that point */
|
|
||||||
Point.prototype.shiftOutwards = function (that, distance) {
|
|
||||||
if (that instanceof Point !== true)
|
|
||||||
this.raise.warning(
|
|
||||||
'Called `Point.shiftOutwards(that, distance)` but `that` is not a `Point` object'
|
|
||||||
)
|
|
||||||
if (typeof distance !== 'number')
|
|
||||||
this.raise.warning(
|
|
||||||
'Called `Point.shiftOutwards(that, distance)` but `distance` is not a number'
|
|
||||||
)
|
|
||||||
this.check()
|
|
||||||
that.check()
|
|
||||||
|
|
||||||
return this.shiftTowards(that, this.dist(that) + distance)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a deep copy of this */
|
|
||||||
Point.prototype.clone = function () {
|
Point.prototype.clone = function () {
|
||||||
this.check()
|
this.__check()
|
||||||
const clone = new Point(this.x, this.y, this.debug).withRaise(this.raise)
|
const clone = new Point(this.x, this.y).__withLog(this.log)
|
||||||
clone.attributes = this.attributes.clone()
|
clone.attributes = this.attributes.clone()
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Applies a translate transform */
|
/**
|
||||||
|
* returns an copy of this Point (coordinates only)
|
||||||
|
*
|
||||||
|
* @return {Point} copy - The copied Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.copy = function () {
|
||||||
|
return new Point(this.__check().x, this.y).__withLog(this.log)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distance between this Point and that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point instance to calculate the distance to
|
||||||
|
* @return {float} distance - The distance between this Point and that Point
|
||||||
|
*/
|
||||||
|
Point.prototype.dist = function (that) {
|
||||||
|
const dx = this.__check().x - that.__check().x
|
||||||
|
const dy = this.y - that.y
|
||||||
|
|
||||||
|
return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distance along the X-axis between this Point and that Point (delta X)
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to which to calcuate the X delta
|
||||||
|
* @return {float} slote - The X delta
|
||||||
|
*/
|
||||||
|
Point.prototype.dx = function (that) {
|
||||||
|
return that.__check().x - this.__check().x
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distance along the Y-axis between this Point and that Point (delta Y)
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to which to calcuate the Y delta
|
||||||
|
* @return {float} slote - The Y delta
|
||||||
|
*/
|
||||||
|
Point.prototype.dy = function (that) {
|
||||||
|
return that.__check().y - this.__check().y
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mirrors this Point around the X value of that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to flip around
|
||||||
|
* @return {Point} flopped - The new flipped Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.flipX = function (that = false) {
|
||||||
|
this.__check()
|
||||||
|
if (that) {
|
||||||
|
if (that instanceof Point !== true)
|
||||||
|
this.log.warning('Called `Point.rotate(deg,that)` but `that` is not a `Point` object')
|
||||||
|
that.__check()
|
||||||
|
}
|
||||||
|
if (that === false || that.x === 0) return new Point(this.x * -1, this.y).__withLog(this.log)
|
||||||
|
else return new Point(that.x + this.dx(that), this.y).__withLog(this.log)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mirrors this Point around the Y value of that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to flip around
|
||||||
|
* @return {Point} flipped - The new flipped Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.flipY = function (that = false) {
|
||||||
|
this.__check()
|
||||||
|
if (that) {
|
||||||
|
if (that instanceof Point !== true)
|
||||||
|
this.log.warning('Called `Point.flipY(that)` but `that` is not a `Point` object')
|
||||||
|
that.__check()
|
||||||
|
}
|
||||||
|
if (that === false || that.y === 0) return new Point(this.x, this.y * -1).__withLog(this.log)
|
||||||
|
else return new Point(this.x, that.y + this.dy(that)).__withLog(this.lo)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate this Point deg around that Point
|
||||||
|
*
|
||||||
|
* @param {float} deg - The degrees to rotate
|
||||||
|
* @param {Point} that - The Point instance to rotate around
|
||||||
|
* @return {Point} rotated - The rotated Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.rotate = function (deg, that) {
|
||||||
|
if (typeof deg !== 'number')
|
||||||
|
this.log.warning('Called `Point.rotate(deg,that)` but `deg` is not a number')
|
||||||
|
if (that instanceof Point !== true)
|
||||||
|
this.log.warning('Called `Point.rotate(deg,that)` but `that` is not a `Point` object')
|
||||||
|
const radius = this.__check().dist(that.__check())
|
||||||
|
const angle = this.angle(that)
|
||||||
|
const x = that.x + radius * Math.cos(deg2rad(angle + deg)) * -1
|
||||||
|
const y = that.y + radius * Math.sin(deg2rad(angle + deg))
|
||||||
|
|
||||||
|
return new Point(x, y).__withLog(this.log)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A chainable way to add a circle at a Point
|
||||||
|
*
|
||||||
|
* @param {float} radius - The circle radius
|
||||||
|
* @param {string} className - The CSS classes to apply to the circle
|
||||||
|
* @return {Point} this - The Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.setCircle = function (radius = false, className = false) {
|
||||||
|
if (radius) this.attributes.set('data-circle', radius)
|
||||||
|
if (className) this.attributes.set('data-circle-class', className)
|
||||||
|
|
||||||
|
return this.__check()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A chainable way to add text to a Point
|
||||||
|
*
|
||||||
|
* @param {string} text - The text to add to the Point
|
||||||
|
* @param {string} className - The CSS classes to apply to the text
|
||||||
|
* @return {Point} this - The Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.setText = function (text = '', className = false) {
|
||||||
|
this.attributes.set('data-text', text)
|
||||||
|
if (className) this.attributes.set('data-text-class', className)
|
||||||
|
|
||||||
|
return this.__check()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shifts this Point distance in the deg direction
|
||||||
|
*
|
||||||
|
* @param {float} deg - The angle to shift towards
|
||||||
|
* @param {float} dist - The distance to shift
|
||||||
|
* @return {Point} shifted - The new shifted Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.shift = function (deg, dist) {
|
||||||
|
if (typeof deg !== 'number') this.log.warning('Called `Point.shift` but `deg` is not a number')
|
||||||
|
if (typeof dist !== 'number')
|
||||||
|
this.log.warning('Called `Point.shift` but `distance` is not a number')
|
||||||
|
let p = this.__check().copy()
|
||||||
|
p.x += dist
|
||||||
|
|
||||||
|
return p.rotate(deg, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shifts this Point a fraction in the direction of that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to shift towards
|
||||||
|
* @param {float} fraction - The fraction to shift
|
||||||
|
* @return {Point} shifted - The new shifted Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.shiftFractionTowards = function (that, fraction) {
|
||||||
|
if (that instanceof Point !== true)
|
||||||
|
this.log.warning(
|
||||||
|
'Called `Point.shiftFractionTowards(that, fraction)` but `that` is not a `Point` object'
|
||||||
|
)
|
||||||
|
if (typeof fraction !== 'number')
|
||||||
|
this.log.warning('Called `Point.shiftFractionTowards` but `fraction` is not a number')
|
||||||
|
|
||||||
|
return this.__check().shiftTowards(that.__check(), this.dist(that) * fraction)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shifts this Point outwards from that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to shift outwards from
|
||||||
|
* @param {float} distance - The distance to shift
|
||||||
|
* @return {Point} shifted - The new shifted Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.shiftOutwards = function (that, distance) {
|
||||||
|
if (that instanceof Point !== true)
|
||||||
|
this.log.warning(
|
||||||
|
'Called `Point.shiftOutwards(that, distance)` but `that` is not a `Point` object'
|
||||||
|
)
|
||||||
|
if (typeof distance !== 'number')
|
||||||
|
this.log.warning('Called `Point.shiftOutwards(that, distance)` but `distance` is not a number')
|
||||||
|
this.__check()
|
||||||
|
that.__check()
|
||||||
|
|
||||||
|
return this.__check().shiftTowards(that.__check(), this.dist(that) + distance)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shifts this Point distance in the direction of that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to short towards
|
||||||
|
* @param {float} dist - The distance to shift
|
||||||
|
* @return {Point} shifted - The new shifted Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.shiftTowards = function (that, dist) {
|
||||||
|
if (typeof dist !== 'number')
|
||||||
|
this.log.warning('Called `Point.shiftTowards` but `distance` is not a number')
|
||||||
|
if (that instanceof Point !== true)
|
||||||
|
this.log.warning(
|
||||||
|
'Called `Point.shiftTowards(that, distance)` but `that` is not a `Point` object'
|
||||||
|
)
|
||||||
|
|
||||||
|
return this.__check().shift(this.angle(that.__check()), dist)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this Point has the same coordinates as that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to compare coordinates with
|
||||||
|
* @return {bool} result - True if the Points' coordinates match, false when they do not
|
||||||
|
*/
|
||||||
|
Point.prototype.sitsOn = function (that) {
|
||||||
|
if (that instanceof Point !== true)
|
||||||
|
this.log.warning('Called `Point.sitsOn(that)` but `that` is not a `Point` object')
|
||||||
|
if (this.__check().x === that.__check().x && this.y === that.y) return true
|
||||||
|
else return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this Point has roughtly the same coordinates as that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point to compare coordinates with
|
||||||
|
* @return {bool} result - True if the Points' coordinates roughty match, false when they do not
|
||||||
|
*/
|
||||||
|
Point.prototype.sitsRoughlyOn = function (that) {
|
||||||
|
if (that instanceof Point !== true)
|
||||||
|
this.log.warning('Called `Point.sitsRoughlyOn(that)` but `that` is not a `Point` object')
|
||||||
|
if (
|
||||||
|
Math.round(this.__check().x) === Math.round(that.__check().x) &&
|
||||||
|
Math.round(this.y) === Math.round(that.y)
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
else return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns slope of a line made by this Point and that Point
|
||||||
|
*
|
||||||
|
* @param {Point} that - The Point that forms the line together with this Point
|
||||||
|
* @return {float} slote - The slope of the line made by this Point and that Point
|
||||||
|
*/
|
||||||
|
Point.prototype.slope = function (that) {
|
||||||
|
return (that.__check().y - this.__check().y) / (that.x - this.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Point instance with a translate transform applied
|
||||||
|
*
|
||||||
|
* @param {float} x - The X-value of the translate transform
|
||||||
|
* @param {float} y - The Y-value of the translate transform
|
||||||
|
* @return {Point} translated - The translated Point instance
|
||||||
|
*/
|
||||||
Point.prototype.translate = function (x, y) {
|
Point.prototype.translate = function (x, y) {
|
||||||
this.check()
|
this.__check()
|
||||||
if (typeof x !== 'number')
|
if (typeof x !== 'number')
|
||||||
this.raise.warning('Called `Point.translate(x,y)` but `x` is not a number')
|
this.log.warning('Called `Point.translate(x,y)` but `x` is not a number')
|
||||||
if (typeof y !== 'number')
|
if (typeof y !== 'number')
|
||||||
this.raise.warning('Called `Point.translate(x,y)` but `y` is not a number')
|
this.log.warning('Called `Point.translate(x,y)` but `y` is not a number')
|
||||||
const p = this.copy()
|
const p = this.copy()
|
||||||
p.x += x
|
p.x += x
|
||||||
p.y += y
|
p.y += y
|
||||||
|
@ -232,18 +321,67 @@ Point.prototype.translate = function (x, y) {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Chainable way to set the data-text property (and optional class) */
|
//////////////////////////////////////////////
|
||||||
Point.prototype.setText = function (text = '', className = false) {
|
// PRIVATE METHODS //
|
||||||
this.attributes.set('data-text', text)
|
//////////////////////////////////////////////
|
||||||
if (className) this.attributes.set('data-text-class', className)
|
|
||||||
|
/**
|
||||||
|
* Checks the Points coordinates, and raises a warning when they are invalid
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {object} this - The Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.__check = function () {
|
||||||
|
if (typeof this.x !== 'number') this.log.warning('X value of `Point` is not a number')
|
||||||
|
if (typeof this.y !== 'number') this.log.warning('Y value of `Point` is not a number')
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Chainable way to set the data-circle property (and optional class) */
|
/**
|
||||||
Point.prototype.setCircle = function (radius = false, className = false) {
|
* Adds a logging instance so the Point can log
|
||||||
if (radius) this.attributes.set('data-circle', radius)
|
*
|
||||||
if (className) this.attributes.set('data-circle-class', className)
|
* @private
|
||||||
|
* @param {object} log - An object holding the logging methods
|
||||||
|
* @return {object} this - The Point instance
|
||||||
|
*/
|
||||||
|
Point.prototype.__withLog = function (log = false) {
|
||||||
|
if (log) Object.defineProperty(this, 'log', { value: log })
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// PUBLIC STATIC METHODS //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ready-to-proxy that logs when things aren't exactly ok
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {object} points - The points object to proxy
|
||||||
|
* @param {object} log - The logging object
|
||||||
|
* @return {object} proxy - The object that is ready to be proxied
|
||||||
|
*/
|
||||||
|
export function pointsProxy(points, log) {
|
||||||
|
return {
|
||||||
|
get: function (...args) {
|
||||||
|
return Reflect.get(...args)
|
||||||
|
},
|
||||||
|
set: (points, name, value) => {
|
||||||
|
// Constructor checks
|
||||||
|
if (value instanceof Point !== true)
|
||||||
|
log.warning(`\`points.${name}\` was set with a value that is not a \`Point\` object`)
|
||||||
|
if (value.x == null || !__isCoord(value.x))
|
||||||
|
log.warning(`\`points.${name}\` was set with a \`x\` parameter that is not a \`number\``)
|
||||||
|
if (value.y == null || !__isCoord(value.y))
|
||||||
|
log.warning(`\`points.${name}\` was set with a \`y\` parameter that is not a \`number\``)
|
||||||
|
try {
|
||||||
|
value.name = name
|
||||||
|
} catch (err) {
|
||||||
|
log.warning(`Could not set \`name\` property on \`points.${name}\``)
|
||||||
|
}
|
||||||
|
return (points[name] = value)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,38 @@
|
||||||
import { Attributes } from './attributes.mjs'
|
import { Attributes } from './attributes.mjs'
|
||||||
|
import { Point } from './point.mjs'
|
||||||
|
|
||||||
export function Snippet(def, anchor, debug = false) {
|
//////////////////////////////////////////////
|
||||||
|
// CONSTRUCTOR //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a Snippet
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {string} def - The id of the snippet in the SVG defs section
|
||||||
|
* @param {Point} anchor - The Point to anchor this Snippet on
|
||||||
|
* @return {Snippet} this - The Snippet instance
|
||||||
|
*/
|
||||||
|
export function Snippet(def, anchor) {
|
||||||
this.def = def
|
this.def = def
|
||||||
this.anchor = anchor
|
this.anchor = anchor
|
||||||
this.attributes = new Attributes()
|
this.attributes = new Attributes()
|
||||||
Object.defineProperty(this, 'debug', { value: debug, configurable: true })
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds the raise method for a snippet not created through the proxy **/
|
//////////////////////////////////////////////
|
||||||
Snippet.prototype.withRaise = function (raise = false) {
|
// PUBLIC METHODS //
|
||||||
if (raise) Object.defineProperty(this, 'raise', { value: raise })
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
return this
|
/**
|
||||||
}
|
* Chainable way to add an attribute
|
||||||
/** Adds an attribute. This is here to make this call chainable in assignment */
|
*
|
||||||
|
* @param {string} name - Name of the attribute to add
|
||||||
|
* @param {string} value - Value of the attribute to add
|
||||||
|
* @param {bool} overwrite - Whether to overwrite an existing attrubute or not
|
||||||
|
* @return {Snippet} this - The Snippet instance
|
||||||
|
*/
|
||||||
Snippet.prototype.attr = function (name, value, overwrite = false) {
|
Snippet.prototype.attr = function (name, value, overwrite = false) {
|
||||||
if (overwrite) this.attributes.set(name, value)
|
if (overwrite) this.attributes.set(name, value)
|
||||||
else this.attributes.add(name, value)
|
else this.attributes.add(name, value)
|
||||||
|
@ -23,10 +40,69 @@ Snippet.prototype.attr = function (name, value, overwrite = false) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a deep copy of this */
|
/**
|
||||||
|
* Returns a deep copy of this snippet
|
||||||
|
*
|
||||||
|
* @return {Snippet} clone - A clone of this Snippet instance
|
||||||
|
*/
|
||||||
Snippet.prototype.clone = function () {
|
Snippet.prototype.clone = function () {
|
||||||
let clone = new Snippet(this.def, this.anchor.clone(), this.debug).withRaise(this.raise)
|
let clone = new Snippet(this.def, this.anchor.clone()).__withLog(this.log)
|
||||||
clone.attributes = this.attributes.clone()
|
clone.attributes = this.attributes.clone()
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// PRIVATE METHODS //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the log method for a snippet not created through the proxy
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {Snippet} this - The Snippet instance
|
||||||
|
*/
|
||||||
|
Snippet.prototype.__withLog = function (log = false) {
|
||||||
|
if (log) Object.defineProperty(this, 'log', { value: log })
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// PUBLIC STATIC METHODS //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ready-to-proxy that logs when things aren't exactly ok
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {object} snippets - The snippets object to proxy
|
||||||
|
* @param {object} log - The logging object
|
||||||
|
* @return {object} proxy - The object that is ready to be proxied
|
||||||
|
*/
|
||||||
|
export function snippetsProxy(snippets, log) {
|
||||||
|
return {
|
||||||
|
get: function (...args) {
|
||||||
|
return Reflect.get(...args)
|
||||||
|
},
|
||||||
|
set: (snippets, name, value) => {
|
||||||
|
// Constructor checks
|
||||||
|
if (value instanceof Snippet !== true)
|
||||||
|
log.warning(`\`snippets.${name}\` was set with a value that is not a \`Snippet\` object`)
|
||||||
|
if (typeof value.def !== 'string')
|
||||||
|
log.warning(
|
||||||
|
`\`snippets.${name}\` was set with a \`def\` parameter that is not a \`string\``
|
||||||
|
)
|
||||||
|
if (value.anchor instanceof Point !== true)
|
||||||
|
log.warning(
|
||||||
|
`\`snippets.${name}\` was set with an \`anchor\` parameter that is not a \`Point\``
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
value.name = name
|
||||||
|
} catch (err) {
|
||||||
|
log.warning(`Could not set \`name\` property on \`snippets.${name}\``)
|
||||||
|
}
|
||||||
|
return (snippets[name] = value)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import * as utils from './utils.mjs'
|
||||||
|
|
||||||
export function Stack(name = null) {
|
export function Stack(name = null) {
|
||||||
// Non-enumerable properties
|
// Non-enumerable properties
|
||||||
utils.addNonEnumProp(this, 'freeId', 0)
|
utils.__addNonEnumProp(this, 'freeId', 0)
|
||||||
utils.addNonEnumProp(this, 'layout', { move: { x: 0, y: 0 } })
|
utils.__addNonEnumProp(this, 'layout', { move: { x: 0, y: 0 } })
|
||||||
|
|
||||||
// Enumerable properties
|
// Enumerable properties
|
||||||
this.attributes = new Attributes()
|
this.attributes = new Attributes()
|
||||||
|
@ -36,33 +36,13 @@ Stack.prototype.getPartNames = function () {
|
||||||
return [...this.parts].map((p) => p.name)
|
return [...this.parts].map((p) => p.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Homes the stack so that its top left corner is in (0,0) */
|
|
||||||
//Stack.prototype.home = function () {
|
|
||||||
// const parts = this.getPartList()
|
|
||||||
// if (parts.length < 1) return this
|
|
||||||
// for (const part of this.getPartList()) {
|
|
||||||
// part.home()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (parts.length === 1) {
|
|
||||||
// this.topLeft = part.topLeft
|
|
||||||
// this.bottomRigth = part.bottomRight
|
|
||||||
// this.width = part.width
|
|
||||||
// this.height = part.height
|
|
||||||
//
|
|
||||||
// return this
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return this.boundary()
|
|
||||||
//}
|
|
||||||
|
|
||||||
/** Calculates the stack's bounding box and sets it */
|
/** Calculates the stack's bounding box and sets it */
|
||||||
Stack.prototype.home = function () {
|
Stack.prototype.home = function () {
|
||||||
if (this.topLeft) return this // Cached
|
if (this.topLeft) return this // Cached
|
||||||
this.topLeft = new Point(Infinity, Infinity)
|
this.topLeft = new Point(Infinity, Infinity)
|
||||||
this.bottomRight = new Point(-Infinity, -Infinity)
|
this.bottomRight = new Point(-Infinity, -Infinity)
|
||||||
for (const part of this.getPartList()) {
|
for (const part of this.getPartList()) {
|
||||||
part.boundary()
|
part.__boundary()
|
||||||
if (part.topLeft.x < this.topLeft.x) this.topLeft.x = part.topLeft.x
|
if (part.topLeft.x < this.topLeft.x) this.topLeft.x = part.topLeft.x
|
||||||
if (part.topLeft.y < this.topLeft.y) this.topLeft.y = part.topLeft.y
|
if (part.topLeft.y < this.topLeft.y) this.topLeft.y = part.topLeft.y
|
||||||
if (part.bottomRight.x > this.bottomRight.x) this.bottomRight.x = part.bottomRight.x
|
if (part.bottomRight.x > this.bottomRight.x) this.bottomRight.x = part.bottomRight.x
|
||||||
|
@ -76,8 +56,11 @@ Stack.prototype.home = function () {
|
||||||
if (this.bottomRight.y === -Infinity) this.bottomRight.y = 0
|
if (this.bottomRight.y === -Infinity) this.bottomRight.y = 0
|
||||||
|
|
||||||
// Add margin
|
// Add margin
|
||||||
let margin = this.context.settings.margin
|
let margin = 0
|
||||||
if (this.context.settings.paperless && margin < 10) margin = 10
|
for (const set in this.context.settings) {
|
||||||
|
if (this.context.settings[set].margin > margin) margin = this.context.settings[set].margin
|
||||||
|
if (this.context.settings[set].paperless && margin < 10) margin = 10
|
||||||
|
}
|
||||||
this.topLeft.x -= margin
|
this.topLeft.x -= margin
|
||||||
this.topLeft.y -= margin
|
this.topLeft.y -= margin
|
||||||
this.bottomRight.x += margin
|
this.bottomRight.x += margin
|
||||||
|
@ -141,4 +124,24 @@ Stack.prototype.generateTransform = function (transforms) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Homes the stack so that its top left corner is in (0,0) */
|
||||||
|
//Stack.prototype.home = function () {
|
||||||
|
// const parts = this.getPartList()
|
||||||
|
// if (parts.length < 1) return this
|
||||||
|
// for (const part of this.getPartList()) {
|
||||||
|
// part.home()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (parts.length === 1) {
|
||||||
|
// this.topLeft = part.topLeft
|
||||||
|
// this.bottomRigth = part.bottomRight
|
||||||
|
// this.width = part.width
|
||||||
|
// this.height = part.height
|
||||||
|
//
|
||||||
|
// return this
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return this.boundary()
|
||||||
|
//}
|
||||||
|
|
||||||
export default Stack
|
export default Stack
|
||||||
|
|
|
@ -2,8 +2,20 @@ import set from 'lodash.set'
|
||||||
import unset from 'lodash.unset'
|
import unset from 'lodash.unset'
|
||||||
import get from 'lodash.get'
|
import get from 'lodash.get'
|
||||||
|
|
||||||
|
// Don't allow setting of these top-level keys in the store
|
||||||
const avoid = ['set', 'setIfUnset', 'push', 'unset', 'get', 'extend']
|
const avoid = ['set', 'setIfUnset', 'push', 'unset', 'get', 'extend']
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// CONSTRUCTOR //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a Store
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {Array} methods - Any methods to add to the store
|
||||||
|
* @return {Store} this - The Store instance
|
||||||
|
*/
|
||||||
export function Store(methods = []) {
|
export function Store(methods = []) {
|
||||||
/*
|
/*
|
||||||
* Default logging methods
|
* Default logging methods
|
||||||
|
@ -40,7 +52,16 @@ export function Store(methods = []) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Extends the store with additional methods */
|
//////////////////////////////////////////////
|
||||||
|
// PUBLIC METHODS //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend the store with additional methods
|
||||||
|
*
|
||||||
|
* @param {function} method - Method to add to the store (variadic)
|
||||||
|
* @return {Store} this - The Store instance
|
||||||
|
*/
|
||||||
Store.prototype.extend = function (...methods) {
|
Store.prototype.extend = function (...methods) {
|
||||||
for (const [path, method] of methods) {
|
for (const [path, method] of methods) {
|
||||||
if (avoid.indexOf(method[0]) !== -1) {
|
if (avoid.indexOf(method[0]) !== -1) {
|
||||||
|
@ -54,29 +75,29 @@ Store.prototype.extend = function (...methods) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set key at path to value */
|
/**
|
||||||
Store.prototype.set = function (path, value) {
|
* Retrieve a key from the store
|
||||||
if (typeof value === 'undefined') {
|
*
|
||||||
this.log.warning(`Store.set(value) on key \`${path}\`, but value is undefined`)
|
* @param {string|array} path - Path to the key
|
||||||
|
* @param {mixed} dflt - Default method to return if key is undefined
|
||||||
|
* @return {mixed} value - The value stored under key
|
||||||
|
*/
|
||||||
|
Store.prototype.get = function (path, dflt) {
|
||||||
|
const val = get(this, path, dflt)
|
||||||
|
if (typeof val === 'undefined') {
|
||||||
|
this.log.warning(`Store.get(key) on key \`${path}\`, which is undefined`)
|
||||||
}
|
}
|
||||||
set(this, path, value)
|
|
||||||
|
|
||||||
return this
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set key at path to value, but only if it's not currently set */
|
/**
|
||||||
Store.prototype.setIfUnset = function (path, value) {
|
* Adds a value to an array stored under path
|
||||||
if (typeof value === 'undefined') {
|
*
|
||||||
this.log.warning(`Store.setIfUnset(value) on key \`${path}\`, but value is undefined`)
|
* @param {string|array} path - Path to the key
|
||||||
}
|
* @param {mixed} values - One or more values to add (variadic)
|
||||||
if (typeof get(this, path) === 'undefined') {
|
* @return {Store} this - The Store instance
|
||||||
return set(this, path, value)
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Adds a value to an array stored under path */
|
|
||||||
Store.prototype.push = function (path, ...values) {
|
Store.prototype.push = function (path, ...values) {
|
||||||
const arr = get(this, path)
|
const arr = get(this, path)
|
||||||
if (Array.isArray(arr)) {
|
if (Array.isArray(arr)) {
|
||||||
|
@ -88,19 +109,49 @@ Store.prototype.push = function (path, ...values) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove the key at path */
|
/**
|
||||||
|
* Set key at path to value
|
||||||
|
*
|
||||||
|
* @param {string|array} path - Path to the key
|
||||||
|
* @param {mixed} value - The value to set
|
||||||
|
* @return {Store} this - The Store instance
|
||||||
|
*/
|
||||||
|
Store.prototype.set = function (path, value) {
|
||||||
|
if (typeof value === 'undefined') {
|
||||||
|
this.log.warning(`Store.set(value) on key \`${path}\`, but value is undefined`)
|
||||||
|
}
|
||||||
|
set(this, path, value)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set key at path to value, but only if it's not currently set
|
||||||
|
*
|
||||||
|
* @param {string|array} path - Path to the key
|
||||||
|
* @param {mixed} value - The value to set
|
||||||
|
* @return {Store} this - The Store instance
|
||||||
|
*/
|
||||||
|
Store.prototype.setIfUnset = function (path, value) {
|
||||||
|
if (typeof value === 'undefined') {
|
||||||
|
this.log.warning(`Store.setIfUnset(value) on key \`${path}\`, but value is undefined`)
|
||||||
|
}
|
||||||
|
if (typeof get(this, path) === 'undefined') {
|
||||||
|
return set(this, path, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the key at path
|
||||||
|
*
|
||||||
|
* @param {string|array} path - Path to the key
|
||||||
|
* @param {mixed} value - The value to set
|
||||||
|
* @return {Store} this - The Store instance
|
||||||
|
*/
|
||||||
Store.prototype.unset = function (path) {
|
Store.prototype.unset = function (path) {
|
||||||
unset(this, path)
|
unset(this, path)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retrieve a key */
|
|
||||||
Store.prototype.get = function (path, dflt) {
|
|
||||||
const val = get(this, path, dflt)
|
|
||||||
if (typeof val === 'undefined') {
|
|
||||||
this.log.warning(`Store.get(key) on key \`${path}\`, which is undefined`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,16 +1,27 @@
|
||||||
import { Attributes } from './attributes.mjs'
|
import { Attributes } from './attributes.mjs'
|
||||||
import { addNonEnumProp, round } from './utils.mjs'
|
import { __addNonEnumProp, round } from './utils.mjs'
|
||||||
import { version } from '../data.mjs'
|
import { version } from '../data.mjs'
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// CONSTRUCTOR //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for an Svg
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {Patern} pattern - The Pattern object to render
|
||||||
|
* @return {Svg} this - The Path instance
|
||||||
|
*/
|
||||||
export function Svg(pattern) {
|
export function Svg(pattern) {
|
||||||
// Non-enumerable properties
|
// Non-enumerable properties
|
||||||
addNonEnumProp(this, 'openGroups', [])
|
__addNonEnumProp(this, 'openGroups', [])
|
||||||
addNonEnumProp(this, 'layout', {})
|
__addNonEnumProp(this, 'layout', {})
|
||||||
addNonEnumProp(this, 'freeId', 0)
|
__addNonEnumProp(this, 'freeId', 0)
|
||||||
addNonEnumProp(this, 'body', '')
|
__addNonEnumProp(this, 'body', '')
|
||||||
addNonEnumProp(this, 'style', '')
|
__addNonEnumProp(this, 'style', '')
|
||||||
addNonEnumProp(this, 'defs', '')
|
__addNonEnumProp(this, 'defs', '')
|
||||||
addNonEnumProp(this, 'prefix', '<?xml version="1.0" encoding="UTF-8" standalone="no"?>')
|
__addNonEnumProp(this, 'prefix', '<?xml version="1.0" encoding="UTF-8" standalone="no"?>')
|
||||||
|
|
||||||
// Enumerable properties
|
// Enumerable properties
|
||||||
this.pattern = pattern // Needed to expose pattern to hooks
|
this.pattern = pattern // Needed to expose pattern to hooks
|
||||||
|
@ -23,18 +34,107 @@ export function Svg(pattern) {
|
||||||
this.attributes.add('freesewing', version)
|
this.attributes.add('freesewing', version)
|
||||||
}
|
}
|
||||||
|
|
||||||
Svg.prototype.runHooks = function (hookName, data = false) {
|
//////////////////////////////////////////////
|
||||||
if (data === false) data = this
|
// PUBLIC METHODS //
|
||||||
let hooks = this.hooks[hookName]
|
//////////////////////////////////////////////
|
||||||
if (hooks.length > 0) {
|
|
||||||
for (let hook of hooks) {
|
/**
|
||||||
hook.method(data, hook.data)
|
* Renders a drafted Pattern as SVG
|
||||||
|
*
|
||||||
|
* @param {Pattern} pattern - The pattern to render
|
||||||
|
* @return {string} svg - The rendered SVG output
|
||||||
|
*/
|
||||||
|
Svg.prototype.render = function (pattern) {
|
||||||
|
this.idPrefix = pattern.settings.idPrefix
|
||||||
|
this.__runHooks('preRender')
|
||||||
|
pattern.__runHooks('postLayout')
|
||||||
|
if (!pattern.settings.embed) {
|
||||||
|
this.attributes.add('width', round(pattern.width) + 'mm')
|
||||||
|
this.attributes.add('height', round(pattern.height) + 'mm')
|
||||||
|
}
|
||||||
|
this.attributes.add('viewBox', `0 0 ${pattern.width} ${pattern.height}`)
|
||||||
|
this.head = this.__renderHead()
|
||||||
|
this.tail = this.__renderTail()
|
||||||
|
this.svg = ''
|
||||||
|
this.layout = {} // Reset layout
|
||||||
|
for (let partId in pattern.parts) {
|
||||||
|
let part = pattern.parts[partId]
|
||||||
|
if (part.render) {
|
||||||
|
let partSvg = this.__renderPart(part)
|
||||||
|
this.layout[partId] = {
|
||||||
|
svg: partSvg,
|
||||||
|
transform: part.attributes.getAsArray('transform'),
|
||||||
|
}
|
||||||
|
this.svg += this.__openGroup(`${this.idPrefix}part-${partId}`, part.attributes)
|
||||||
|
this.svg += partSvg
|
||||||
|
this.svg += this.__closeGroup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.svg = this.prefix + this.__renderSvgTag() + this.head + this.svg + this.tail
|
||||||
|
this.__runHooks('postRender')
|
||||||
|
|
||||||
|
return this.svg
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runs insertText hooks */
|
//////////////////////////////////////////////
|
||||||
Svg.prototype.insertText = function (text) {
|
// PRIVATE METHODS //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns SVG markup to close a group
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {string} svg - The SVG markup to open a group
|
||||||
|
*/
|
||||||
|
Svg.prototype.__closeGroup = function () {
|
||||||
|
this.__outdent()
|
||||||
|
|
||||||
|
return `${this.__nl()}</g>${this.__nl()}<!-- end of group #${this.openGroups.pop()} -->`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes text for SVG output
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} text - The text to escape
|
||||||
|
* @return {string} escaped - The escaped text
|
||||||
|
*/
|
||||||
|
Svg.prototype.__escapeText = function (text) {
|
||||||
|
return text.replace(/"/g, '“')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returs an unused ID
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {numer} id - The next free ID
|
||||||
|
*/
|
||||||
|
Svg.prototype.__getId = function () {
|
||||||
|
this.freeId += 1
|
||||||
|
|
||||||
|
return '' + this.freeId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases indentation by 1
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {Svg} this - The Svg instance
|
||||||
|
*/
|
||||||
|
Svg.prototype.__indent = function () {
|
||||||
|
this.tabs += 1
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the insertText lifecycle hook(s)
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} text - The text to insert
|
||||||
|
* @return {Svg} this - The Svg instance
|
||||||
|
*/
|
||||||
|
Svg.prototype.__insertText = function (text) {
|
||||||
if (this.hooks.insertText.length > 0) {
|
if (this.hooks.insertText.length > 0) {
|
||||||
for (let hook of this.hooks.insertText)
|
for (let hook of this.hooks.insertText)
|
||||||
text = hook.method(this.pattern.settings.locale, text, hook.data)
|
text = hook.method(this.pattern.settings.locale, text, hook.data)
|
||||||
|
@ -43,198 +143,188 @@ Svg.prototype.insertText = function (text) {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Renders a draft object as SVG */
|
/**
|
||||||
Svg.prototype.render = function (pattern) {
|
* Returns SVG markup for a linebreak + indentation
|
||||||
this.idPrefix = pattern.settings.idPrefix
|
*
|
||||||
this.runHooks('preRender')
|
* @private
|
||||||
pattern.runHooks('postLayout')
|
* @return {string} svg - The Svg markup for a linebreak + indentation
|
||||||
if (!pattern.settings.embed) {
|
*/
|
||||||
this.attributes.add('width', round(pattern.width) + 'mm')
|
Svg.prototype.__nl = function () {
|
||||||
this.attributes.add('height', round(pattern.height) + 'mm')
|
return '\n' + this.__tab()
|
||||||
}
|
|
||||||
this.attributes.add('viewBox', `0 0 ${pattern.width} ${pattern.height}`)
|
|
||||||
this.head = this.renderHead()
|
|
||||||
this.tail = this.renderTail()
|
|
||||||
this.svg = ''
|
|
||||||
this.layout = {} // Reset layout
|
|
||||||
for (let partId in pattern.parts) {
|
|
||||||
let part = pattern.parts[partId]
|
|
||||||
if (part.render) {
|
|
||||||
let partSvg = this.renderPart(part)
|
|
||||||
this.layout[partId] = {
|
|
||||||
svg: partSvg,
|
|
||||||
transform: part.attributes.getAsArray('transform'),
|
|
||||||
}
|
|
||||||
this.svg += this.openGroup(`${this.idPrefix}part-${partId}`, part.attributes)
|
|
||||||
this.svg += partSvg
|
|
||||||
this.svg += this.closeGroup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.svg = this.prefix + this.renderSvgTag() + this.head + this.svg + this.tail
|
|
||||||
this.runHooks('postRender')
|
|
||||||
|
|
||||||
return this.svg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Renders SVG head section */
|
/**
|
||||||
Svg.prototype.renderHead = function () {
|
* Decreases indentation by 1
|
||||||
let svg = this.renderStyle()
|
*
|
||||||
svg += this.renderScript()
|
* @private
|
||||||
svg += this.renderDefs()
|
* @return {Svg} this - The Svg instance
|
||||||
svg += this.openGroup(this.idPrefix + 'container')
|
*/
|
||||||
|
Svg.prototype.__outdent = function () {
|
||||||
|
this.tabs -= 1
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns SVG markup to open a group
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {text} id - The group id
|
||||||
|
* @param {Attributes} attributes - Any other attributes for the group
|
||||||
|
* @return {string} svg - The SVG markup to open a group
|
||||||
|
*/
|
||||||
|
Svg.prototype.__openGroup = function (id, attributes = false) {
|
||||||
|
let svg = this.__nl() + this.__nl()
|
||||||
|
svg += `<!-- Start of group #${id} -->`
|
||||||
|
svg += this.__nl()
|
||||||
|
svg += `<g id="${id}"`
|
||||||
|
if (attributes) svg += ` ${attributes.render()}`
|
||||||
|
svg += '>'
|
||||||
|
this.__indent()
|
||||||
|
this.openGroups.push(id)
|
||||||
|
|
||||||
return svg
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Renders SVG closing section */
|
/**
|
||||||
Svg.prototype.renderTail = function () {
|
* Returns SVG markup for a circle
|
||||||
let svg = ''
|
*
|
||||||
svg += this.closeGroup()
|
* @private
|
||||||
svg += this.nl() + '</svg>'
|
* @param {Point} point - The Point instance that holds the circle data
|
||||||
|
* @return {string} svg - The SVG markup for the circle
|
||||||
return svg
|
*/
|
||||||
|
Svg.prototype.__renderCircle = function (point) {
|
||||||
|
return `<circle cx="${round(point.x)}" cy="${round(point.y)}" r="${point.attributes.get(
|
||||||
|
'data-circle'
|
||||||
|
)}" ${point.attributes.renderIfPrefixIs('data-circle-')}></circle>`
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns SVG code for the opening SVG tag */
|
/**
|
||||||
Svg.prototype.renderSvgTag = function () {
|
* Returns SVG markup for the defs block
|
||||||
let svg = '<svg'
|
*
|
||||||
this.indent()
|
* @private
|
||||||
svg += this.nl() + this.attributes.render()
|
* @return {string} svg - The SVG markup for the defs block
|
||||||
this.outdent()
|
*/
|
||||||
svg += this.nl() + '>' + this.nl()
|
Svg.prototype.__renderDefs = function () {
|
||||||
|
|
||||||
return svg
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns SVG code for the style block */
|
|
||||||
Svg.prototype.renderStyle = function () {
|
|
||||||
let svg = '<style type="text/css"> <![CDATA[ '
|
|
||||||
this.indent()
|
|
||||||
svg += this.nl() + this.style
|
|
||||||
this.outdent()
|
|
||||||
svg += this.nl() + ']]>' + this.nl() + '</style>' + this.nl()
|
|
||||||
return svg
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns SVG code for the script block */
|
|
||||||
Svg.prototype.renderScript = function () {
|
|
||||||
let svg = '<script type="text/javascript"> <![CDATA['
|
|
||||||
this.indent()
|
|
||||||
svg += this.nl() + this.script
|
|
||||||
this.outdent()
|
|
||||||
svg += this.nl() + ']]>' + this.nl() + '</script>' + this.nl()
|
|
||||||
|
|
||||||
return svg
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns SVG code for the defs block */
|
|
||||||
Svg.prototype.renderDefs = function () {
|
|
||||||
let svg = '<defs>'
|
let svg = '<defs>'
|
||||||
this.indent()
|
this.__indent()
|
||||||
svg += this.nl() + this.defs
|
svg += this.__nl() + this.defs
|
||||||
this.outdent()
|
this.__outdent()
|
||||||
svg += this.nl() + '</defs>' + this.nl()
|
svg += this.__nl() + '</defs>' + this.__nl()
|
||||||
|
|
||||||
return svg
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns SVG code for a Part object */
|
/**
|
||||||
Svg.prototype.renderPart = function (part) {
|
* Returns SVG markup for the head section
|
||||||
let svg = ''
|
*
|
||||||
for (let key in part.paths) {
|
* @private
|
||||||
let path = part.paths[key]
|
* @return {string} svg - The SVG markup for the head section
|
||||||
if (path.render) svg += this.renderPath(path)
|
*/
|
||||||
}
|
Svg.prototype.__renderHead = function () {
|
||||||
for (let key in part.points) {
|
let svg = this.__renderStyle()
|
||||||
if (part.points[key].attributes.get('data-text')) {
|
svg += this.__renderScript()
|
||||||
svg += this.renderText(part.points[key])
|
svg += this.__renderDefs()
|
||||||
}
|
svg += this.__openGroup(this.idPrefix + 'container')
|
||||||
if (part.points[key].attributes.get('data-circle')) {
|
|
||||||
svg += this.renderCircle(part.points[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let key in part.snippets) {
|
|
||||||
let snippet = part.snippets[key]
|
|
||||||
svg += this.renderSnippet(snippet, part)
|
|
||||||
}
|
|
||||||
|
|
||||||
return svg
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns SVG code for a Path object */
|
/**
|
||||||
Svg.prototype.renderPath = function (path) {
|
* Returns SVG markup for a Path object
|
||||||
if (!path.attributes.get('id')) path.attributes.add('id', this.idPrefix + this.getId())
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Path} part - The Path instance to render
|
||||||
|
* @return {string} svg - The SVG markup for the Path object
|
||||||
|
*/
|
||||||
|
Svg.prototype.__renderPath = function (path) {
|
||||||
|
if (!path.attributes.get('id')) path.attributes.add('id', this.idPrefix + this.__getId())
|
||||||
path.attributes.set('d', path.asPathstring())
|
path.attributes.set('d', path.asPathstring())
|
||||||
|
|
||||||
return `${this.nl()}<path ${path.attributes.render()} />${this.renderPathText(path)}`
|
return `${this.__nl()}<path ${path.attributes.render()} />${this.__renderPathText(path)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
Svg.prototype.renderPathText = function (path) {
|
/**
|
||||||
|
* Returns SVG markup for the text on a Path object
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Path} path - The Path instance that holds the text render
|
||||||
|
* @return {string} svg - The SVG markup for the text on a Path object
|
||||||
|
*/
|
||||||
|
Svg.prototype.__renderPathText = function (path) {
|
||||||
let text = path.attributes.get('data-text')
|
let text = path.attributes.get('data-text')
|
||||||
if (!text) return ''
|
if (!text) return ''
|
||||||
else this.text = this.insertText(text)
|
else this.text = this.__insertText(text)
|
||||||
let attributes = path.attributes.renderIfPrefixIs('data-text-')
|
let attributes = path.attributes.renderIfPrefixIs('data-text-')
|
||||||
// Sadly aligning text along a patch can't be done in CSS only
|
// Sadly aligning text along a patch can't be done in CSS only
|
||||||
let offset = ''
|
let offset = ''
|
||||||
let align = path.attributes.get('data-text-class')
|
let align = path.attributes.get('data-text-class')
|
||||||
if (align && align.indexOf('center') > -1) offset = ' startOffset="50%" '
|
if (align && align.indexOf('center') > -1) offset = ' startOffset="50%" '
|
||||||
else if (align && align.indexOf('right') > -1) offset = ' startOffset="100%" '
|
else if (align && align.indexOf('right') > -1) offset = ' startOffset="100%" '
|
||||||
let svg = this.nl() + '<text>'
|
let svg = this.__nl() + '<text>'
|
||||||
this.indent()
|
this.__indent()
|
||||||
svg += `<textPath xlink:href="#${path.attributes.get(
|
svg += `<textPath xlink:href="#${path.attributes.get(
|
||||||
'id'
|
'id'
|
||||||
)}" ${offset}><tspan ${attributes}>${this.escapeText(this.text)}</tspan></textPath>`
|
)}" ${offset}><tspan ${attributes}>${this.__escapeText(this.text)}</tspan></textPath>`
|
||||||
this.outdent()
|
this.__outdent()
|
||||||
svg += this.nl() + '</text>'
|
svg += this.__nl() + '</text>'
|
||||||
|
|
||||||
return svg
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
Svg.prototype.renderText = function (point) {
|
/**
|
||||||
let text = point.attributes.getAsArray('data-text')
|
* Returns SVG markup for a Part object
|
||||||
if (text !== false) {
|
*
|
||||||
let joint = ''
|
* @private
|
||||||
for (let string of text) {
|
* @param {Part} part - The Part instance to render
|
||||||
this.text = this.insertText(string)
|
* @return {string} svg - The SVG markup for the Part object
|
||||||
joint += this.text + ' '
|
*/
|
||||||
}
|
Svg.prototype.__renderPart = function (part) {
|
||||||
this.text = this.insertText(joint)
|
let svg = ''
|
||||||
|
for (let key in part.paths) {
|
||||||
|
let path = part.paths[key]
|
||||||
|
if (path.render) svg += this.__renderPath(path)
|
||||||
}
|
}
|
||||||
point.attributes.set('data-text-x', round(point.x))
|
for (let key in part.points) {
|
||||||
point.attributes.set('data-text-y', round(point.y))
|
if (part.points[key].attributes.get('data-text')) {
|
||||||
let lineHeight =
|
svg += this.__renderText(part.points[key])
|
||||||
point.attributes.get('data-text-lineheight') || 6 * (this.pattern.settings.scale || 1)
|
}
|
||||||
point.attributes.remove('data-text-lineheight')
|
if (part.points[key].attributes.get('data-circle')) {
|
||||||
let svg = `${this.nl()}<text ${point.attributes.renderIfPrefixIs('data-text-')}>`
|
svg += this.__renderCircle(part.points[key])
|
||||||
this.indent()
|
|
||||||
// Multi-line text?
|
|
||||||
if (this.text.indexOf('\n') !== -1) {
|
|
||||||
let lines = this.text.split('\n')
|
|
||||||
svg += `<tspan>${lines.shift()}</tspan>`
|
|
||||||
for (let line of lines) {
|
|
||||||
svg += `<tspan x="${round(point.x)}" dy="${lineHeight}">${line}</tspan>`
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
svg += `<tspan>${this.escapeText(this.text)}</tspan>`
|
|
||||||
}
|
}
|
||||||
this.outdent()
|
for (let key in part.snippets) {
|
||||||
svg += this.nl() + '</text>'
|
let snippet = part.snippets[key]
|
||||||
|
svg += this.__renderSnippet(snippet, part)
|
||||||
|
}
|
||||||
|
|
||||||
return svg
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
Svg.prototype.escapeText = function (text) {
|
/**
|
||||||
return text.replace(/"/g, '“')
|
* Returns SVG markup for the script block
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {string} svg - The SVG markup for the script block
|
||||||
|
*/
|
||||||
|
Svg.prototype.__renderScript = function () {
|
||||||
|
let svg = '<script type="text/javascript"> <![CDATA['
|
||||||
|
this.__indent()
|
||||||
|
svg += this.__nl() + this.script
|
||||||
|
this.__outdent()
|
||||||
|
svg += this.__nl() + ']]>' + this.__nl() + '</script>' + this.__nl()
|
||||||
|
|
||||||
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
Svg.prototype.renderCircle = function (point) {
|
/**
|
||||||
return `<circle cx="${round(point.x)}" cy="${round(point.y)}" r="${point.attributes.get(
|
* Returns SVG markup for a snippet
|
||||||
'data-circle'
|
*
|
||||||
)}" ${point.attributes.renderIfPrefixIs('data-circle-')}></circle>`
|
* @private
|
||||||
}
|
* @param {Snippet} snippet - The Snippet instance to render
|
||||||
|
* @return {string} svg - The SVG markup for the snippet
|
||||||
/** Returns SVG code for a snippet */
|
*/
|
||||||
Svg.prototype.renderSnippet = function (snippet) {
|
Svg.prototype.__renderSnippet = function (snippet) {
|
||||||
let x = round(snippet.anchor.x)
|
let x = round(snippet.anchor.x)
|
||||||
let y = round(snippet.anchor.y)
|
let y = round(snippet.anchor.y)
|
||||||
let scale = snippet.attributes.get('data-scale') || 1
|
let scale = snippet.attributes.get('data-scale') || 1
|
||||||
|
@ -248,7 +338,7 @@ Svg.prototype.renderSnippet = function (snippet) {
|
||||||
if (rotate) {
|
if (rotate) {
|
||||||
snippet.attributes.add('transform', `rotate(${rotate}, ${x}, ${y})`)
|
snippet.attributes.add('transform', `rotate(${rotate}, ${x}, ${y})`)
|
||||||
}
|
}
|
||||||
let svg = this.nl()
|
let svg = this.__nl()
|
||||||
svg += `<use x="${x}" y="${y}" `
|
svg += `<use x="${x}" y="${y}" `
|
||||||
svg += `xlink:href="#${snippet.def}" ${snippet.attributes.render()}>`
|
svg += `xlink:href="#${snippet.def}" ${snippet.attributes.render()}>`
|
||||||
svg += '</use>'
|
svg += '</use>'
|
||||||
|
@ -256,34 +346,116 @@ Svg.prototype.renderSnippet = function (snippet) {
|
||||||
return svg
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns SVG code to open a group */
|
/**
|
||||||
Svg.prototype.openGroup = function (id, attributes = false) {
|
* Returns SVG markup for the style block
|
||||||
let svg = this.nl() + this.nl()
|
*
|
||||||
svg += `<!-- Start of group #${id} -->`
|
* @private
|
||||||
svg += this.nl()
|
* @return {string} svg - The SVG markup for the style block
|
||||||
svg += `<g id="${id}"`
|
*/
|
||||||
if (attributes) svg += ` ${attributes.render()}`
|
Svg.prototype.__renderStyle = function () {
|
||||||
svg += '>'
|
let svg = '<style type="text/css"> <![CDATA[ '
|
||||||
this.indent()
|
this.__indent()
|
||||||
this.openGroups.push(id)
|
svg += this.__nl() + this.style
|
||||||
|
this.__outdent()
|
||||||
|
svg += this.__nl() + ']]>' + this.__nl() + '</style>' + this.__nl()
|
||||||
|
return svg
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns SVG markup for the opening SVG tag
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {string} svg - The SVG markup for the SVG tag
|
||||||
|
*/
|
||||||
|
Svg.prototype.__renderSvgTag = function () {
|
||||||
|
let svg = '<svg'
|
||||||
|
this.__indent()
|
||||||
|
svg += this.__nl() + this.attributes.render()
|
||||||
|
this.__outdent()
|
||||||
|
svg += this.__nl() + '>' + this.__nl()
|
||||||
|
|
||||||
return svg
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns SVG code to close a group */
|
/**
|
||||||
Svg.prototype.closeGroup = function () {
|
* Returns SVG markup for the closing section
|
||||||
this.outdent()
|
*
|
||||||
|
* @private
|
||||||
|
* @return {string} svg - The SVG markup for the closing section
|
||||||
|
*/
|
||||||
|
Svg.prototype.__renderTail = function () {
|
||||||
|
let svg = ''
|
||||||
|
svg += this.__closeGroup()
|
||||||
|
svg += this.__nl() + '</svg>'
|
||||||
|
|
||||||
return `${this.nl()}</g>${this.nl()}<!-- end of group #${this.openGroups.pop()} -->`
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a linebreak + identation */
|
/**
|
||||||
Svg.prototype.nl = function () {
|
* Returns SVG markup for text
|
||||||
return '\n' + this.tab()
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Point} point - The Point instance that holds the text render
|
||||||
|
* @return {string} svg - The SVG markup for text
|
||||||
|
*/
|
||||||
|
Svg.prototype.__renderText = function (point) {
|
||||||
|
let text = point.attributes.getAsArray('data-text')
|
||||||
|
if (text !== false) {
|
||||||
|
let joint = ''
|
||||||
|
for (let string of text) {
|
||||||
|
this.text = this.__insertText(string)
|
||||||
|
joint += this.text + ' '
|
||||||
|
}
|
||||||
|
this.text = this.__insertText(joint)
|
||||||
|
}
|
||||||
|
point.attributes.set('data-text-x', round(point.x))
|
||||||
|
point.attributes.set('data-text-y', round(point.y))
|
||||||
|
let lineHeight =
|
||||||
|
point.attributes.get('data-text-lineheight') || 6 * (this.pattern.settings.scale || 1)
|
||||||
|
point.attributes.remove('data-text-lineheight')
|
||||||
|
let svg = `${this.__nl()}<text ${point.attributes.renderIfPrefixIs('data-text-')}>`
|
||||||
|
this.__indent()
|
||||||
|
// Multi-line text?
|
||||||
|
if (this.text.indexOf('\n') !== -1) {
|
||||||
|
let lines = this.text.split('\n')
|
||||||
|
svg += `<tspan>${lines.shift()}</tspan>`
|
||||||
|
for (let line of lines) {
|
||||||
|
svg += `<tspan x="${round(point.x)}" dy="${lineHeight}">${line}</tspan>`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
svg += `<tspan>${this.__escapeText(this.text)}</tspan>`
|
||||||
|
}
|
||||||
|
this.__outdent()
|
||||||
|
svg += this.__nl() + '</text>'
|
||||||
|
|
||||||
|
return svg
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns indentation */
|
/**
|
||||||
Svg.prototype.tab = function () {
|
* Runs SVG lifecycle hooks
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} hookName - The lifecycle hook to run
|
||||||
|
* @param {mixed} data - Any data to pass to the hook method
|
||||||
|
* @return {string} svg - The SVG markup for the indentation
|
||||||
|
*/
|
||||||
|
Svg.prototype.__runHooks = function (hookName, data = false) {
|
||||||
|
if (data === false) data = this
|
||||||
|
let hooks = this.hooks[hookName]
|
||||||
|
if (hooks.length > 0) {
|
||||||
|
for (let hook of hooks) {
|
||||||
|
hook.method(data, hook.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns SVG markup for indentation
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {string} svg - The SVG markup for the indentation
|
||||||
|
*/
|
||||||
|
Svg.prototype.__tab = function () {
|
||||||
let space = ''
|
let space = ''
|
||||||
for (let i = 0; i < this.tabs; i++) {
|
for (let i = 0; i < this.tabs; i++) {
|
||||||
space += ' '
|
space += ' '
|
||||||
|
@ -291,20 +463,3 @@ Svg.prototype.tab = function () {
|
||||||
|
|
||||||
return space
|
return space
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Increases indentation by 1 */
|
|
||||||
Svg.prototype.indent = function () {
|
|
||||||
this.tabs += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Decreases indentation by 1 */
|
|
||||||
Svg.prototype.outdent = function () {
|
|
||||||
this.tabs -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns an unused ID */
|
|
||||||
Svg.prototype.getId = function () {
|
|
||||||
this.freeId += 1
|
|
||||||
|
|
||||||
return '' + this.freeId
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
77
packages/core/tests/multi.test.mjs
Normal file
77
packages/core/tests/multi.test.mjs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import chai from 'chai'
|
||||||
|
import { Design } from '../src/index.mjs'
|
||||||
|
|
||||||
|
const expect = chai.expect
|
||||||
|
|
||||||
|
if (!expect) console.log('shut up eslint REMOVE')
|
||||||
|
|
||||||
|
describe('Multisets', () => {
|
||||||
|
describe('FIXME', () => {
|
||||||
|
const partA = {
|
||||||
|
name: 'test.partA',
|
||||||
|
measurements: ['head'],
|
||||||
|
options: {
|
||||||
|
size: { pct: 40, min: 20, max: 80 },
|
||||||
|
},
|
||||||
|
draft: ({ points, Point, paths, Path, part, store, measurements, options }) => {
|
||||||
|
store.set('size', measurements.head * options.size)
|
||||||
|
points.from = new Point(0, 0)
|
||||||
|
points.to = new Point(0, store.get('size'))
|
||||||
|
paths.line = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
//stack: 'box',
|
||||||
|
}
|
||||||
|
const partB = {
|
||||||
|
name: 'test.partB',
|
||||||
|
measurements: ['head'],
|
||||||
|
after: partA,
|
||||||
|
draft: ({ points, Point, paths, Path, part, store }) => {
|
||||||
|
points.from = new Point(0, store.get('size'))
|
||||||
|
points.to = new Point(store.get('size'), store.get('size'))
|
||||||
|
paths.line = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
//stack: 'box',
|
||||||
|
}
|
||||||
|
const partC = {
|
||||||
|
name: 'test.partC',
|
||||||
|
after: partB,
|
||||||
|
draft: ({ points, Point, paths, Path, part, store }) => {
|
||||||
|
points.from = new Point(store.get('size'), store.get('size'))
|
||||||
|
points.to = new Point(store.get('size'), 0)
|
||||||
|
paths.line = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
//stack: 'box',
|
||||||
|
}
|
||||||
|
const partD = {
|
||||||
|
name: 'test.partD',
|
||||||
|
after: partC,
|
||||||
|
draft: ({ points, Point, paths, Path, part, store }) => {
|
||||||
|
points.from = new Point(store.get('size'), 0)
|
||||||
|
points.to = new Point(0, 0)
|
||||||
|
paths.line = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
// stack: 'box',
|
||||||
|
}
|
||||||
|
|
||||||
|
const Pattern = new Design({
|
||||||
|
data: {
|
||||||
|
name: 'test',
|
||||||
|
version: '1.2.3',
|
||||||
|
},
|
||||||
|
parts: [partD],
|
||||||
|
})
|
||||||
|
const pattern = new Pattern([
|
||||||
|
{
|
||||||
|
measurements: { head: 400 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
measurements: { head: 400 },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
pattern.draft()
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,5 +1,5 @@
|
||||||
import chai from 'chai'
|
import chai from 'chai'
|
||||||
import { Design, Pattern } from '../src/index.mjs'
|
import { Design, Part } from '../src/index.mjs'
|
||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
|
@ -20,21 +20,25 @@ describe('Part', () => {
|
||||||
expect(typeof dp.context).to.equal('object')
|
expect(typeof dp.context).to.equal('object')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should return a function from macroClosure', () => {
|
it('Should return a function from __macroClosure', () => {
|
||||||
const pattern = new Pattern()
|
const part = new Part()
|
||||||
const part = pattern.__createPartWithContext()
|
expect(typeof part.__macroClosure()).to.equal('function')
|
||||||
expect(typeof part.macroClosure()).to.equal('function')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not run an unknown macro', () => {
|
it('Should not run an unknown macro', () => {
|
||||||
const pattern = new Pattern()
|
const part = new Part()
|
||||||
const part = pattern.__createPartWithContext()
|
const macro = part.__macroClosure()
|
||||||
const macro = part.macroClosure()
|
|
||||||
expect(macro('unknown')).to.equal(undefined)
|
expect(macro('unknown')).to.equal(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should register and run a macro', () => {
|
it('Should register and run a macro', () => {
|
||||||
const pattern = new Pattern()
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({ part, macro }) => {
|
||||||
|
macro('test', { x: 123, y: 456 })
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
}
|
||||||
const plugin = {
|
const plugin = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
version: '0.1-test',
|
version: '0.1-test',
|
||||||
|
@ -45,39 +49,34 @@ describe('Part', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
pattern.use(plugin)
|
const design = new Design({ parts: [part], plugins: [plugin] })
|
||||||
const part = pattern.__createPartWithContext()
|
const pattern = new design()
|
||||||
const macro = part.macroClosure()
|
pattern.draft()
|
||||||
macro('test', { x: 123, y: 456 })
|
expect(pattern.parts[0].test.points.macro.x).to.equal(123)
|
||||||
expect(part.points.macro.x).to.equal(123)
|
expect(pattern.parts[0].test.points.macro.y).to.equal(456)
|
||||||
expect(part.points.macro.y).to.equal(456)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should return a free ID', () => {
|
it('Should return a free ID', () => {
|
||||||
const pattern = new Pattern()
|
const part = new Part()
|
||||||
const part = pattern.__createPartWithContext()
|
|
||||||
const free = part.getId()
|
const free = part.getId()
|
||||||
expect(part.getId()).to.equal('' + (parseInt(free) + 1))
|
expect(part.getId()).to.equal('' + (parseInt(free) + 1))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should return a function from unitsClosure', () => {
|
it('Should return a function from __unitsClosure', () => {
|
||||||
const pattern = new Pattern()
|
const part = new Part()
|
||||||
const part = pattern.__createPartWithContext()
|
expect(typeof part.__unitsClosure()).to.equal('function')
|
||||||
expect(typeof part.unitsClosure()).to.equal('function')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should convert units', () => {
|
it('Should convert units', () => {
|
||||||
const design = new Design()
|
const part = new Part()
|
||||||
const pattern = new design()
|
part.context = { settings: { units: 'metric' } }
|
||||||
const part = pattern.__createPartWithContext()
|
const units = part.__unitsClosure()
|
||||||
const units = part.unitsClosure()
|
expect(units(123.456)).to.equal('12.35cm')
|
||||||
expect(units(123.456)).to.equal('12.35cm')
|
expect(units(123.456)).to.equal('12.35cm')
|
||||||
expect(part.units(123.456)).to.equal('12.35cm')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should set part attributes', () => {
|
it('Should set part attributes', () => {
|
||||||
const pattern = new Pattern()
|
const part = new Part()
|
||||||
const part = pattern.__createPartWithContext()
|
|
||||||
part.attr('foo', 'bar')
|
part.attr('foo', 'bar')
|
||||||
expect(part.attributes.get('foo')).to.equal('bar')
|
expect(part.attributes.get('foo')).to.equal('bar')
|
||||||
part.attr('foo', 'baz')
|
part.attr('foo', 'baz')
|
||||||
|
@ -87,60 +86,72 @@ describe('Part', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when setting a non-Point value in points', () => {
|
it('Should raise a warning when setting a non-Point value in points', () => {
|
||||||
const design = new Design()
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({ points, part }) => {
|
||||||
|
points.a = 'banana'
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const design = new Design({ parts: [part] })
|
||||||
const pattern = new design()
|
const pattern = new design()
|
||||||
const part = pattern.__createPartWithContext()
|
pattern.draft()
|
||||||
pattern.init()
|
expect(pattern.stores[0].logs.warning.length).to.equal(4)
|
||||||
const { points } = part.shorthand()
|
expect(pattern.stores[0].logs.warning[0]).to.equal(
|
||||||
points.a = 'banana'
|
|
||||||
expect(pattern.store.logs.warning.length).to.equal(4)
|
|
||||||
expect(pattern.store.logs.warning[0]).to.equal(
|
|
||||||
'`points.a` was set with a value that is not a `Point` object'
|
'`points.a` was set with a value that is not a `Point` object'
|
||||||
)
|
)
|
||||||
expect(pattern.store.logs.warning[1]).to.equal(
|
expect(pattern.stores[0].logs.warning[1]).to.equal(
|
||||||
'`points.a` was set with a `x` parameter that is not a `number`'
|
'`points.a` was set with a `x` parameter that is not a `number`'
|
||||||
)
|
)
|
||||||
expect(pattern.store.logs.warning[2]).to.equal(
|
expect(pattern.stores[0].logs.warning[2]).to.equal(
|
||||||
'`points.a` was set with a `y` parameter that is not a `number`'
|
'`points.a` was set with a `y` parameter that is not a `number`'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when setting a non-Snippet value in snippets', () => {
|
it('Should raise a warning when setting a non-Snippet value in snippets', () => {
|
||||||
const design = new Design()
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({ snippets, part }) => {
|
||||||
|
snippets.a = 'banana'
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const design = new Design({ parts: [part] })
|
||||||
const pattern = new design()
|
const pattern = new design()
|
||||||
const part = pattern.__createPartWithContext()
|
pattern.draft()
|
||||||
pattern.init()
|
expect(pattern.stores[0].logs.warning.length).to.equal(4)
|
||||||
const { snippets } = part.shorthand()
|
expect(pattern.stores[0].logs.warning[0]).to.equal(
|
||||||
snippets.a = 'banana'
|
|
||||||
expect(pattern.store.logs.warning.length).to.equal(4)
|
|
||||||
expect(pattern.store.logs.warning[0]).to.equal(
|
|
||||||
'`snippets.a` was set with a value that is not a `Snippet` object'
|
'`snippets.a` was set with a value that is not a `Snippet` object'
|
||||||
)
|
)
|
||||||
expect(pattern.store.logs.warning[1]).to.equal(
|
expect(pattern.stores[0].logs.warning[1]).to.equal(
|
||||||
'`snippets.a` was set with a `def` parameter that is not a `string`'
|
'`snippets.a` was set with a `def` parameter that is not a `string`'
|
||||||
)
|
)
|
||||||
expect(pattern.store.logs.warning[2]).to.equal(
|
expect(pattern.stores[0].logs.warning[2]).to.equal(
|
||||||
'`snippets.a` was set with an `anchor` parameter that is not a `Point`'
|
'`snippets.a` was set with an `anchor` parameter that is not a `Point`'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should calculate the part boundary', () => {
|
it('Should calculate the part boundary', () => {
|
||||||
const design = new Design()
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({ points, Point, paths, Path, part }) => {
|
||||||
|
points.from = new Point(123, 456)
|
||||||
|
points.to = new Point(19, 76)
|
||||||
|
paths.test = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const design = new Design({ parts: [part] })
|
||||||
const pattern = new design()
|
const pattern = new design()
|
||||||
const part = pattern.__createPartWithContext()
|
pattern.draft()
|
||||||
pattern.init()
|
const boundary = pattern.parts[0].test.__boundary()
|
||||||
const short = part.shorthand()
|
const { topLeft, bottomRight, width, height } = boundary
|
||||||
part.points.from = new short.Point(123, 456)
|
expect(topLeft.x).to.equal(19)
|
||||||
part.points.to = new short.Point(19, 76)
|
expect(topLeft.y).to.equal(76)
|
||||||
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
|
expect(bottomRight.x).to.equal(123)
|
||||||
let boundary = part.boundary()
|
expect(bottomRight.y).to.equal(456)
|
||||||
expect(boundary.topLeft.x).to.equal(19)
|
expect(width).to.equal(104)
|
||||||
expect(boundary.topLeft.y).to.equal(76)
|
expect(height).to.equal(380)
|
||||||
expect(boundary.bottomRight.x).to.equal(123)
|
|
||||||
expect(boundary.bottomRight.y).to.equal(456)
|
|
||||||
boundary = part.boundary()
|
|
||||||
expect(boundary.width).to.equal(104)
|
|
||||||
expect(boundary.height).to.equal(380)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -176,7 +187,6 @@ describe('Part', () => {
|
||||||
part.home()
|
part.home()
|
||||||
expect(part.attributes.get('transform')).to.equal(false)
|
expect(part.attributes.get('transform')).to.equal(false)
|
||||||
})
|
})
|
||||||
*/
|
|
||||||
it('Should run hooks', () => {
|
it('Should run hooks', () => {
|
||||||
let count = 0
|
let count = 0
|
||||||
const design = new Design()
|
const design = new Design()
|
||||||
|
@ -192,20 +202,25 @@ describe('Part', () => {
|
||||||
part.runHooks('preDraft')
|
part.runHooks('preDraft')
|
||||||
expect(count).to.equal(1)
|
expect(count).to.equal(1)
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
it('Should get the units closure to raise a debug when passing a non-number', () => {
|
it('Units closure should log a warning when passing a non-number', () => {
|
||||||
const design = new Design()
|
const part = {
|
||||||
const pattern = new design({ margin: 5 })
|
name: 'test',
|
||||||
const part = pattern.__createPartWithContext()
|
draft: ({ units, part }) => {
|
||||||
pattern.init()
|
units('a')
|
||||||
const short = part.shorthand()
|
return part
|
||||||
short.units('a')
|
},
|
||||||
expect(pattern.store.logs.warning.length).to.equal(1)
|
}
|
||||||
expect(pattern.store.logs.warning[0]).to.equal(
|
const design = new Design({ parts: [part] })
|
||||||
|
const pattern = new design()
|
||||||
|
pattern.draft()
|
||||||
|
expect(pattern.stores[0].logs.warning.length).to.equal(1)
|
||||||
|
expect(pattern.stores[0].logs.warning[0]).to.equal(
|
||||||
'Calling `units(value)` but `value` is not a number (`string`)'
|
'Calling `units(value)` but `value` is not a number (`string`)'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
/*
|
||||||
describe('isEmpty', () => {
|
describe('isEmpty', () => {
|
||||||
it('Should return true if the part has no paths or snippets', () => {
|
it('Should return true if the part has no paths or snippets', () => {
|
||||||
const design = new Design()
|
const design = new Design()
|
||||||
|
@ -272,4 +287,5 @@ describe('Part', () => {
|
||||||
expect(part.isEmpty()).to.be.false
|
expect(part.isEmpty()).to.be.false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
})
|
})
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -78,9 +78,9 @@ describe('Pattern', () => {
|
||||||
only: ['test.partB'],
|
only: ['test.partB'],
|
||||||
})
|
})
|
||||||
pattern.init()
|
pattern.init()
|
||||||
expect(pattern.needs('test.partA')).to.equal(true)
|
expect(pattern.__needs('test.partA')).to.equal(true)
|
||||||
expect(pattern.needs('test.partB')).to.equal(true)
|
expect(pattern.__needs('test.partB')).to.equal(true)
|
||||||
expect(pattern.needs('test.partC')).to.equal(false)
|
expect(pattern.__needs('test.partC')).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should check whether a part is wanted', () => {
|
it('Should check whether a part is wanted', () => {
|
||||||
|
@ -127,9 +127,9 @@ describe('Pattern', () => {
|
||||||
only: ['test.partB'],
|
only: ['test.partB'],
|
||||||
})
|
})
|
||||||
pattern.init()
|
pattern.init()
|
||||||
expect(pattern.wants('test.partA')).to.equal(false)
|
expect(pattern.__wants('test.partA')).to.equal(false)
|
||||||
expect(pattern.wants('test.partB')).to.equal(true)
|
expect(pattern.__wants('test.partB')).to.equal(true)
|
||||||
expect(pattern.wants('test.partC')).to.equal(false)
|
expect(pattern.__wants('test.partC')).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -14,11 +14,11 @@ describe('Pattern', () => {
|
||||||
it('Pattern constructor should add enumerable properties', () => {
|
it('Pattern constructor should add enumerable properties', () => {
|
||||||
const Pattern = new Design()
|
const Pattern = new Design()
|
||||||
const pattern = new Pattern()
|
const pattern = new Pattern()
|
||||||
expect(typeof pattern.settings).to.equal('object')
|
expect(Array.isArray(pattern.settings)).to.equal(true)
|
||||||
|
expect(Array.isArray(pattern.stores)).to.equal(true)
|
||||||
expect(typeof pattern.config).to.equal('object')
|
expect(typeof pattern.config).to.equal('object')
|
||||||
expect(typeof pattern.parts).to.equal('object')
|
expect(typeof pattern.store).to.equal('undefined')
|
||||||
expect(typeof pattern.store).to.equal('object')
|
expect(Object.keys(pattern).length).to.equal(4)
|
||||||
expect(Object.keys(pattern).length).to.equal(5)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern constructor should add non-enumerable properties', () => {
|
it('Pattern constructor should add non-enumerable properties', () => {
|
||||||
|
@ -60,7 +60,7 @@ describe('Pattern', () => {
|
||||||
}
|
}
|
||||||
for (const [key, value] of Object.entries(dflts)) {
|
for (const [key, value] of Object.entries(dflts)) {
|
||||||
if (typeof value === 'object') expect(Object.keys(value).length).to.equal(0)
|
if (typeof value === 'object') expect(Object.keys(value).length).to.equal(0)
|
||||||
else expect(pattern.settings[key]).to.equal(value)
|
else expect(pattern.settings[0][key]).to.equal(value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -154,8 +154,8 @@ describe('Pattern', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern.init() should set config data in the store', () => {
|
it('Pattern.init() should set config data in the store', () => {
|
||||||
expect(pattern.store.get('data.name')).to.equal('test')
|
expect(pattern.stores[0].get('data.name')).to.equal('test')
|
||||||
expect(pattern.store.get('data.version')).to.equal('1.2.3')
|
expect(pattern.stores[0].get('data.version')).to.equal('1.2.3')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern.init() should resolve dependencies', () => {
|
it('Pattern.init() should resolve dependencies', () => {
|
||||||
|
@ -282,58 +282,58 @@ describe('Pattern', () => {
|
||||||
expect(pattern.config.draftOrder[2]).to.equal('partC')
|
expect(pattern.config.draftOrder[2]).to.equal('partC')
|
||||||
expect(pattern.config.draftOrder[3]).to.equal('partR')
|
expect(pattern.config.draftOrder[3]).to.equal('partR')
|
||||||
// Points
|
// Points
|
||||||
expect(pattern.parts.partA.points.a1.x).to.equal(1)
|
expect(pattern.parts[0].partA.points.a1.x).to.equal(1)
|
||||||
expect(pattern.parts.partA.points.a1.y).to.equal(1)
|
expect(pattern.parts[0].partA.points.a1.y).to.equal(1)
|
||||||
expect(pattern.parts.partA.points.a2.x).to.equal(11)
|
expect(pattern.parts[0].partA.points.a2.x).to.equal(11)
|
||||||
expect(pattern.parts.partA.points.a2.y).to.equal(11)
|
expect(pattern.parts[0].partA.points.a2.y).to.equal(11)
|
||||||
expect(pattern.parts.partB.points.b1.x).to.equal(2)
|
expect(pattern.parts[0].partB.points.b1.x).to.equal(2)
|
||||||
expect(pattern.parts.partB.points.b1.y).to.equal(2)
|
expect(pattern.parts[0].partB.points.b1.y).to.equal(2)
|
||||||
expect(pattern.parts.partB.points.b2.x).to.equal(22)
|
expect(pattern.parts[0].partB.points.b2.x).to.equal(22)
|
||||||
expect(pattern.parts.partB.points.b2.y).to.equal(22)
|
expect(pattern.parts[0].partB.points.b2.y).to.equal(22)
|
||||||
expect(pattern.parts.partC.points.c1.x).to.equal(3)
|
expect(pattern.parts[0].partC.points.c1.x).to.equal(3)
|
||||||
expect(pattern.parts.partC.points.c1.y).to.equal(3)
|
expect(pattern.parts[0].partC.points.c1.y).to.equal(3)
|
||||||
expect(pattern.parts.partC.points.c2.x).to.equal(33)
|
expect(pattern.parts[0].partC.points.c2.x).to.equal(33)
|
||||||
expect(pattern.parts.partC.points.c2.y).to.equal(33)
|
expect(pattern.parts[0].partC.points.c2.y).to.equal(33)
|
||||||
expect(pattern.parts.partR.points.r1.x).to.equal(4)
|
expect(pattern.parts[0].partR.points.r1.x).to.equal(4)
|
||||||
expect(pattern.parts.partR.points.r1.y).to.equal(4)
|
expect(pattern.parts[0].partR.points.r1.y).to.equal(4)
|
||||||
expect(pattern.parts.partR.points.r2.x).to.equal(44)
|
expect(pattern.parts[0].partR.points.r2.x).to.equal(44)
|
||||||
expect(pattern.parts.partR.points.r2.y).to.equal(44)
|
expect(pattern.parts[0].partR.points.r2.y).to.equal(44)
|
||||||
// Paths in partA
|
// Paths in partA
|
||||||
expect(pattern.parts.partA.paths.a.ops[0].to.x).to.equal(1)
|
expect(pattern.parts[0].partA.paths.a.ops[0].to.x).to.equal(1)
|
||||||
expect(pattern.parts.partA.paths.a.ops[0].to.y).to.equal(1)
|
expect(pattern.parts[0].partA.paths.a.ops[0].to.y).to.equal(1)
|
||||||
expect(pattern.parts.partA.paths.a.ops[1].to.x).to.equal(11)
|
expect(pattern.parts[0].partA.paths.a.ops[1].to.x).to.equal(11)
|
||||||
expect(pattern.parts.partA.paths.a.ops[1].to.y).to.equal(11)
|
expect(pattern.parts[0].partA.paths.a.ops[1].to.y).to.equal(11)
|
||||||
// Paths in partB
|
// Paths in partB
|
||||||
expect(pattern.parts.partB.paths.a.ops[0].to.x).to.equal(1)
|
expect(pattern.parts[0].partB.paths.a.ops[0].to.x).to.equal(1)
|
||||||
expect(pattern.parts.partB.paths.a.ops[0].to.y).to.equal(1)
|
expect(pattern.parts[0].partB.paths.a.ops[0].to.y).to.equal(1)
|
||||||
expect(pattern.parts.partB.paths.a.ops[1].to.x).to.equal(11)
|
expect(pattern.parts[0].partB.paths.a.ops[1].to.x).to.equal(11)
|
||||||
expect(pattern.parts.partB.paths.a.ops[1].to.y).to.equal(11)
|
expect(pattern.parts[0].partB.paths.a.ops[1].to.y).to.equal(11)
|
||||||
expect(pattern.parts.partB.paths.b.ops[0].to.x).to.equal(2)
|
expect(pattern.parts[0].partB.paths.b.ops[0].to.x).to.equal(2)
|
||||||
expect(pattern.parts.partB.paths.b.ops[0].to.y).to.equal(2)
|
expect(pattern.parts[0].partB.paths.b.ops[0].to.y).to.equal(2)
|
||||||
expect(pattern.parts.partB.paths.b.ops[1].to.x).to.equal(22)
|
expect(pattern.parts[0].partB.paths.b.ops[1].to.x).to.equal(22)
|
||||||
expect(pattern.parts.partB.paths.b.ops[1].to.y).to.equal(22)
|
expect(pattern.parts[0].partB.paths.b.ops[1].to.y).to.equal(22)
|
||||||
// Paths in partC
|
// Paths in partC
|
||||||
expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1)
|
expect(pattern.parts[0].partC.paths.a.ops[0].to.x).to.equal(1)
|
||||||
expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1)
|
expect(pattern.parts[0].partC.paths.a.ops[0].to.y).to.equal(1)
|
||||||
expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11)
|
expect(pattern.parts[0].partC.paths.a.ops[1].to.x).to.equal(11)
|
||||||
expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11)
|
expect(pattern.parts[0].partC.paths.a.ops[1].to.y).to.equal(11)
|
||||||
expect(pattern.parts.partC.paths.b.ops[0].to.x).to.equal(2)
|
expect(pattern.parts[0].partC.paths.b.ops[0].to.x).to.equal(2)
|
||||||
expect(pattern.parts.partC.paths.b.ops[0].to.y).to.equal(2)
|
expect(pattern.parts[0].partC.paths.b.ops[0].to.y).to.equal(2)
|
||||||
expect(pattern.parts.partC.paths.b.ops[1].to.x).to.equal(22)
|
expect(pattern.parts[0].partC.paths.b.ops[1].to.x).to.equal(22)
|
||||||
expect(pattern.parts.partC.paths.b.ops[1].to.y).to.equal(22)
|
expect(pattern.parts[0].partC.paths.b.ops[1].to.y).to.equal(22)
|
||||||
expect(pattern.parts.partC.paths.c.ops[0].to.x).to.equal(3)
|
expect(pattern.parts[0].partC.paths.c.ops[0].to.x).to.equal(3)
|
||||||
expect(pattern.parts.partC.paths.c.ops[0].to.y).to.equal(3)
|
expect(pattern.parts[0].partC.paths.c.ops[0].to.y).to.equal(3)
|
||||||
expect(pattern.parts.partC.paths.c.ops[1].to.x).to.equal(33)
|
expect(pattern.parts[0].partC.paths.c.ops[1].to.x).to.equal(33)
|
||||||
expect(pattern.parts.partC.paths.c.ops[1].to.y).to.equal(33)
|
expect(pattern.parts[0].partC.paths.c.ops[1].to.y).to.equal(33)
|
||||||
// Paths in partR
|
// Paths in partR
|
||||||
expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1)
|
expect(pattern.parts[0].partC.paths.a.ops[0].to.x).to.equal(1)
|
||||||
expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1)
|
expect(pattern.parts[0].partC.paths.a.ops[0].to.y).to.equal(1)
|
||||||
expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11)
|
expect(pattern.parts[0].partC.paths.a.ops[1].to.x).to.equal(11)
|
||||||
expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11)
|
expect(pattern.parts[0].partC.paths.a.ops[1].to.y).to.equal(11)
|
||||||
expect(pattern.parts.partR.paths.r.ops[0].to.x).to.equal(4)
|
expect(pattern.parts[0].partR.paths.r.ops[0].to.x).to.equal(4)
|
||||||
expect(pattern.parts.partR.paths.r.ops[0].to.y).to.equal(4)
|
expect(pattern.parts[0].partR.paths.r.ops[0].to.y).to.equal(4)
|
||||||
expect(pattern.parts.partR.paths.r.ops[1].to.x).to.equal(44)
|
expect(pattern.parts[0].partR.paths.r.ops[1].to.x).to.equal(44)
|
||||||
expect(pattern.parts.partR.paths.r.ops[1].to.y).to.equal(44)
|
expect(pattern.parts[0].partR.paths.r.ops[1].to.y).to.equal(44)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern.init() should resolve nested dependencies', () => {
|
it('Pattern.init() should resolve nested dependencies', () => {
|
||||||
|
@ -428,54 +428,54 @@ describe('Pattern', () => {
|
||||||
expect(pattern.config.draftOrder[2]).to.equal('partC')
|
expect(pattern.config.draftOrder[2]).to.equal('partC')
|
||||||
expect(pattern.config.draftOrder[3]).to.equal('partD')
|
expect(pattern.config.draftOrder[3]).to.equal('partD')
|
||||||
// Points
|
// Points
|
||||||
expect(pattern.parts.partA.points.a1.x).to.equal(1)
|
expect(pattern.parts[0].partA.points.a1.x).to.equal(1)
|
||||||
expect(pattern.parts.partA.points.a1.y).to.equal(1)
|
expect(pattern.parts[0].partA.points.a1.y).to.equal(1)
|
||||||
expect(pattern.parts.partA.points.a2.x).to.equal(11)
|
expect(pattern.parts[0].partA.points.a2.x).to.equal(11)
|
||||||
expect(pattern.parts.partA.points.a2.y).to.equal(11)
|
expect(pattern.parts[0].partA.points.a2.y).to.equal(11)
|
||||||
expect(pattern.parts.partB.points.b1.x).to.equal(2)
|
expect(pattern.parts[0].partB.points.b1.x).to.equal(2)
|
||||||
expect(pattern.parts.partB.points.b1.y).to.equal(2)
|
expect(pattern.parts[0].partB.points.b1.y).to.equal(2)
|
||||||
expect(pattern.parts.partB.points.b2.x).to.equal(22)
|
expect(pattern.parts[0].partB.points.b2.x).to.equal(22)
|
||||||
expect(pattern.parts.partB.points.b2.y).to.equal(22)
|
expect(pattern.parts[0].partB.points.b2.y).to.equal(22)
|
||||||
expect(pattern.parts.partC.points.c1.x).to.equal(3)
|
expect(pattern.parts[0].partC.points.c1.x).to.equal(3)
|
||||||
expect(pattern.parts.partC.points.c1.y).to.equal(3)
|
expect(pattern.parts[0].partC.points.c1.y).to.equal(3)
|
||||||
expect(pattern.parts.partC.points.c2.x).to.equal(33)
|
expect(pattern.parts[0].partC.points.c2.x).to.equal(33)
|
||||||
expect(pattern.parts.partC.points.c2.y).to.equal(33)
|
expect(pattern.parts[0].partC.points.c2.y).to.equal(33)
|
||||||
expect(pattern.parts.partD.points.d1.x).to.equal(4)
|
expect(pattern.parts[0].partD.points.d1.x).to.equal(4)
|
||||||
expect(pattern.parts.partD.points.d1.y).to.equal(4)
|
expect(pattern.parts[0].partD.points.d1.y).to.equal(4)
|
||||||
expect(pattern.parts.partD.points.d2.x).to.equal(44)
|
expect(pattern.parts[0].partD.points.d2.x).to.equal(44)
|
||||||
expect(pattern.parts.partD.points.d2.y).to.equal(44)
|
expect(pattern.parts[0].partD.points.d2.y).to.equal(44)
|
||||||
// Paths in partA
|
// Paths in partA
|
||||||
expect(pattern.parts.partA.paths.a.ops[0].to.x).to.equal(1)
|
expect(pattern.parts[0].partA.paths.a.ops[0].to.x).to.equal(1)
|
||||||
expect(pattern.parts.partA.paths.a.ops[0].to.y).to.equal(1)
|
expect(pattern.parts[0].partA.paths.a.ops[0].to.y).to.equal(1)
|
||||||
expect(pattern.parts.partA.paths.a.ops[1].to.x).to.equal(11)
|
expect(pattern.parts[0].partA.paths.a.ops[1].to.x).to.equal(11)
|
||||||
expect(pattern.parts.partA.paths.a.ops[1].to.y).to.equal(11)
|
expect(pattern.parts[0].partA.paths.a.ops[1].to.y).to.equal(11)
|
||||||
// Paths in partB
|
// Paths in partB
|
||||||
expect(pattern.parts.partB.paths.a.ops[0].to.x).to.equal(1)
|
expect(pattern.parts[0].partB.paths.a.ops[0].to.x).to.equal(1)
|
||||||
expect(pattern.parts.partB.paths.a.ops[0].to.y).to.equal(1)
|
expect(pattern.parts[0].partB.paths.a.ops[0].to.y).to.equal(1)
|
||||||
expect(pattern.parts.partB.paths.a.ops[1].to.x).to.equal(11)
|
expect(pattern.parts[0].partB.paths.a.ops[1].to.x).to.equal(11)
|
||||||
expect(pattern.parts.partB.paths.a.ops[1].to.y).to.equal(11)
|
expect(pattern.parts[0].partB.paths.a.ops[1].to.y).to.equal(11)
|
||||||
expect(pattern.parts.partB.paths.b.ops[0].to.x).to.equal(2)
|
expect(pattern.parts[0].partB.paths.b.ops[0].to.x).to.equal(2)
|
||||||
expect(pattern.parts.partB.paths.b.ops[0].to.y).to.equal(2)
|
expect(pattern.parts[0].partB.paths.b.ops[0].to.y).to.equal(2)
|
||||||
expect(pattern.parts.partB.paths.b.ops[1].to.x).to.equal(22)
|
expect(pattern.parts[0].partB.paths.b.ops[1].to.x).to.equal(22)
|
||||||
expect(pattern.parts.partB.paths.b.ops[1].to.y).to.equal(22)
|
expect(pattern.parts[0].partB.paths.b.ops[1].to.y).to.equal(22)
|
||||||
// Paths in partC
|
// Paths in partC
|
||||||
expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1)
|
expect(pattern.parts[0].partC.paths.a.ops[0].to.x).to.equal(1)
|
||||||
expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1)
|
expect(pattern.parts[0].partC.paths.a.ops[0].to.y).to.equal(1)
|
||||||
expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11)
|
expect(pattern.parts[0].partC.paths.a.ops[1].to.x).to.equal(11)
|
||||||
expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11)
|
expect(pattern.parts[0].partC.paths.a.ops[1].to.y).to.equal(11)
|
||||||
expect(pattern.parts.partC.paths.b.ops[0].to.x).to.equal(2)
|
expect(pattern.parts[0].partC.paths.b.ops[0].to.x).to.equal(2)
|
||||||
expect(pattern.parts.partC.paths.b.ops[0].to.y).to.equal(2)
|
expect(pattern.parts[0].partC.paths.b.ops[0].to.y).to.equal(2)
|
||||||
expect(pattern.parts.partC.paths.b.ops[1].to.x).to.equal(22)
|
expect(pattern.parts[0].partC.paths.b.ops[1].to.x).to.equal(22)
|
||||||
expect(pattern.parts.partC.paths.b.ops[1].to.y).to.equal(22)
|
expect(pattern.parts[0].partC.paths.b.ops[1].to.y).to.equal(22)
|
||||||
expect(pattern.parts.partC.paths.c.ops[0].to.x).to.equal(3)
|
expect(pattern.parts[0].partC.paths.c.ops[0].to.x).to.equal(3)
|
||||||
expect(pattern.parts.partC.paths.c.ops[0].to.y).to.equal(3)
|
expect(pattern.parts[0].partC.paths.c.ops[0].to.y).to.equal(3)
|
||||||
expect(pattern.parts.partC.paths.c.ops[1].to.x).to.equal(33)
|
expect(pattern.parts[0].partC.paths.c.ops[1].to.x).to.equal(33)
|
||||||
expect(pattern.parts.partC.paths.c.ops[1].to.y).to.equal(33)
|
expect(pattern.parts[0].partC.paths.c.ops[1].to.y).to.equal(33)
|
||||||
// Paths in partR
|
// Paths in partR
|
||||||
expect(pattern.parts.partD.paths.d.ops[0].to.x).to.equal(4)
|
expect(pattern.parts[0].partD.paths.d.ops[0].to.x).to.equal(4)
|
||||||
expect(pattern.parts.partD.paths.d.ops[0].to.y).to.equal(4)
|
expect(pattern.parts[0].partD.paths.d.ops[0].to.y).to.equal(4)
|
||||||
expect(pattern.parts.partD.paths.d.ops[1].to.x).to.equal(44)
|
expect(pattern.parts[0].partD.paths.d.ops[1].to.x).to.equal(44)
|
||||||
expect(pattern.parts.partD.paths.d.ops[1].to.y).to.equal(44)
|
expect(pattern.parts[0].partD.paths.d.ops[1].to.y).to.equal(44)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern.init() should load a single plugin', () => {
|
it('Pattern.init() should load a single plugin', () => {
|
||||||
|
@ -635,30 +635,35 @@ describe('Pattern', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should check whether created parts get the pattern context', () => {
|
it('Should check whether created parts get the pattern context', () => {
|
||||||
const Pattern = new Design()
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({ part }) => part,
|
||||||
|
}
|
||||||
|
const Pattern = new Design({ parts: [part] })
|
||||||
const pattern = new Pattern()
|
const pattern = new Pattern()
|
||||||
const part = pattern.__createPartWithContext('test')
|
pattern.draft()
|
||||||
expect(typeof part.context).to.equal('object')
|
const context = pattern.parts[0].test.context
|
||||||
expect(typeof part.context.parts).to.equal('object')
|
expect(typeof context).to.equal('object')
|
||||||
expect(typeof part.context.config).to.equal('object')
|
expect(typeof context.parts).to.equal('object')
|
||||||
expect(typeof part.context.config.options).to.equal('object')
|
expect(typeof context.config).to.equal('object')
|
||||||
expect(typeof part.context.config.data).to.equal('object')
|
expect(typeof context.config.options).to.equal('object')
|
||||||
expect(Array.isArray(part.context.config.measurements)).to.equal(true)
|
expect(typeof pattern.parts[0].test.context.config.data).to.equal('object')
|
||||||
expect(Array.isArray(part.context.config.optionalMeasurements)).to.equal(true)
|
expect(Array.isArray(context.config.measurements)).to.equal(true)
|
||||||
expect(Array.isArray(part.context.config.parts)).to.equal(true)
|
expect(Array.isArray(context.config.optionalMeasurements)).to.equal(true)
|
||||||
expect(Array.isArray(part.context.config.plugins)).to.equal(true)
|
expect(Array.isArray(context.config.parts)).to.equal(true)
|
||||||
expect(part.context.settings).to.equal(pattern.settings)
|
expect(Array.isArray(context.config.plugins)).to.equal(true)
|
||||||
expect(typeof part.context.store).to.equal('object')
|
expect(context.settings).to.equal(pattern.settings[0])
|
||||||
expect(typeof part.context.store.log).to.equal('object')
|
expect(typeof context.store).to.equal('object')
|
||||||
expect(typeof part.context.store.log.debug).to.equal('function')
|
expect(typeof context.store.log).to.equal('object')
|
||||||
expect(typeof part.context.store.log.info).to.equal('function')
|
expect(typeof context.store.log.debug).to.equal('function')
|
||||||
expect(typeof part.context.store.log.warning).to.equal('function')
|
expect(typeof context.store.log.info).to.equal('function')
|
||||||
expect(typeof part.context.store.log.error).to.equal('function')
|
expect(typeof context.store.log.warning).to.equal('function')
|
||||||
expect(typeof part.context.store.logs).to.equal('object')
|
expect(typeof context.store.log.error).to.equal('function')
|
||||||
expect(Array.isArray(part.context.store.logs.debug)).to.equal(true)
|
expect(typeof context.store.logs).to.equal('object')
|
||||||
expect(Array.isArray(part.context.store.logs.info)).to.equal(true)
|
expect(Array.isArray(context.store.logs.debug)).to.equal(true)
|
||||||
expect(Array.isArray(part.context.store.logs.warning)).to.equal(true)
|
expect(Array.isArray(context.store.logs.info)).to.equal(true)
|
||||||
expect(Array.isArray(part.context.store.logs.error)).to.equal(true)
|
expect(Array.isArray(context.store.logs.warning)).to.equal(true)
|
||||||
|
expect(Array.isArray(context.store.logs.error)).to.equal(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -690,27 +695,27 @@ describe('Pattern', () => {
|
||||||
pattern.init()
|
pattern.init()
|
||||||
|
|
||||||
it('Pattern settings should contain percentage options', () => {
|
it('Pattern settings should contain percentage options', () => {
|
||||||
expect(pattern.settings.options.pct).to.equal(0.3)
|
expect(pattern.settings[0].options.pct).to.equal(0.3)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern settings should contain millimeter options', () => {
|
it('Pattern settings should contain millimeter options', () => {
|
||||||
expect(pattern.settings.options.mm).to.equal(12)
|
expect(pattern.settings[0].options.mm).to.equal(12)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern settings should contain degree options', () => {
|
it('Pattern settings should contain degree options', () => {
|
||||||
expect(pattern.settings.options.deg).to.equal(2)
|
expect(pattern.settings[0].options.deg).to.equal(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern settings should contain list options', () => {
|
it('Pattern settings should contain list options', () => {
|
||||||
expect(pattern.settings.options.list).to.equal('d')
|
expect(pattern.settings[0].options.list).to.equal('d')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern settings should contain count options', () => {
|
it('Pattern settings should contain count options', () => {
|
||||||
expect(pattern.settings.options.count).to.equal(4)
|
expect(pattern.settings[0].options.count).to.equal(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern settings should contain bool options', () => {
|
it('Pattern settings should contain bool options', () => {
|
||||||
expect(pattern.settings.options.bool).to.equal(false)
|
expect(pattern.settings[0].options.bool).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern should throw an error for an unknown option', () => {
|
it('Pattern should throw an error for an unknown option', () => {
|
||||||
|
|
|
@ -227,40 +227,40 @@ describe('Point', () => {
|
||||||
expect(p2.y).to.equal(70)
|
expect(p2.y).to.equal(70)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should add raise methods to a point', () => {
|
it('Should add log methods to a point', () => {
|
||||||
const raise = () => 'hello'
|
const log = () => 'hello'
|
||||||
const p1 = new Point(10, 20).withRaise(raise)
|
const p1 = new Point(10, 20).__withLog(log)
|
||||||
expect(p1.raise()).to.equal('hello')
|
expect(p1.log()).to.equal('hello')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning on invalid point coordinates', () => {
|
it('Should log a warning on invalid point coordinates', () => {
|
||||||
const invalid = { x: false, y: false }
|
const invalid = { x: false, y: false }
|
||||||
const raiseX = { warning: () => (invalid.x = true) }
|
const logX = { warning: () => (invalid.x = true) }
|
||||||
const raiseY = { warning: () => (invalid.y = true) }
|
const logY = { warning: () => (invalid.y = true) }
|
||||||
const p1 = new Point('a', 10).withRaise(raiseX)
|
const p1 = new Point('a', 10).__withLog(logX)
|
||||||
const p2 = new Point(20, 'b').withRaise(raiseY)
|
const p2 = new Point(20, 'b').__withLog(logY)
|
||||||
expect(invalid.x).to.equal(false)
|
expect(invalid.x).to.equal(false)
|
||||||
expect(invalid.y).to.equal(false)
|
expect(invalid.y).to.equal(false)
|
||||||
p1.check()
|
p1.__check()
|
||||||
p2.check()
|
p2.__check()
|
||||||
expect(invalid.x).to.equal(true)
|
expect(invalid.x).to.equal(true)
|
||||||
expect(invalid.y).to.equal(true)
|
expect(invalid.y).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning if rotation is not a number', () => {
|
it('Should log a warning if rotation is not a number', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
const p2 = new Point(20, 20).withRaise(raise)
|
const p2 = new Point(20, 20).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
p1.rotate('a', p2)
|
p1.rotate('a', p2)
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning if rotating around what is not a point', () => {
|
it('Should log a warning if rotating around what is not a point', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.rotate(45, 'a')
|
p1.rotate(45, 'a')
|
||||||
|
@ -270,10 +270,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Should raise a warning when flipX'ing around what is not a point", () => {
|
it("Should log a warning when flipX'ing around what is not a point", () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.flipX('a')
|
p1.flipX('a')
|
||||||
|
@ -283,10 +283,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Should raise a warning when flipY'ing around what is not a point", () => {
|
it("Should log a warning when flipY'ing around what is not a point", () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.flipY('a')
|
p1.flipY('a')
|
||||||
|
@ -296,10 +296,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when shifting with a distance that is not a number', () => {
|
it('Should log a warning when shifting with a distance that is not a number', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.shift(0, 'a')
|
p1.shift(0, 'a')
|
||||||
|
@ -309,10 +309,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when shifting with an angle that is not a number', () => {
|
it('Should log a warning when shifting with an angle that is not a number', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.shift('a', 12)
|
p1.shift('a', 12)
|
||||||
|
@ -322,11 +322,11 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when shifting towards with a distance that is not a number', () => {
|
it('Should log a warning when shifting towards with a distance that is not a number', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
const p2 = new Point(20, 20).withRaise(raise)
|
const p2 = new Point(20, 20).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.shiftTowards(p2, 'a')
|
p1.shiftTowards(p2, 'a')
|
||||||
|
@ -336,10 +336,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when shifting towards with a target that is not a point', () => {
|
it('Should log a warning when shifting towards with a target that is not a point', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.shiftTowards('a', 10)
|
p1.shiftTowards('a', 10)
|
||||||
|
@ -349,11 +349,11 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when shifting fraction towards with a distance that is not a number', () => {
|
it('Should log a warning when shifting fraction towards with a distance that is not a number', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
const p2 = new Point(20, 20).withRaise(raise)
|
const p2 = new Point(20, 20).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.shiftFractionTowards(p2, 'a')
|
p1.shiftFractionTowards(p2, 'a')
|
||||||
|
@ -363,10 +363,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when shifting a fraction towards with a target that is not a point', () => {
|
it('Should log a warning when shifting a fraction towards with a target that is not a point', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.shiftFractionTowards('a', 0.1)
|
p1.shiftFractionTowards('a', 0.1)
|
||||||
|
@ -376,11 +376,11 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when shifting outowards with a distance that is not a number', () => {
|
it('Should log a warning when shifting outowards with a distance that is not a number', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
const p2 = new Point(20, 20).withRaise(raise)
|
const p2 = new Point(20, 20).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.shiftOutwards(p2, 'a')
|
p1.shiftOutwards(p2, 'a')
|
||||||
|
@ -390,10 +390,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when shifting a outowards with a target that is not a point', () => {
|
it('Should log a warning when shifting a outowards with a target that is not a point', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.shiftOutwards('a', 0.1)
|
p1.shiftOutwards('a', 0.1)
|
||||||
|
@ -403,10 +403,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when translating with an X-delta that is not a number', () => {
|
it('Should log a warning when translating with an X-delta that is not a number', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.translate('a', 10)
|
p1.translate('a', 10)
|
||||||
|
@ -416,10 +416,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when translating with an Y-delta that is not a number', () => {
|
it('Should log a warning when translating with an Y-delta that is not a number', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.translate(10, 'a')
|
p1.translate(10, 'a')
|
||||||
|
@ -429,10 +429,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when sitsOn receives a non-point', () => {
|
it('Should log a warning when sitsOn receives a non-point', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.sitsOn('a')
|
p1.sitsOn('a')
|
||||||
|
@ -442,10 +442,10 @@ describe('Point', () => {
|
||||||
expect(invalid).to.equal(true)
|
expect(invalid).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should raise a warning when sitsRoughlyOn receives a non-point', () => {
|
it('Should log a warning when sitsRoughlyOn receives a non-point', () => {
|
||||||
let invalid = false
|
let invalid = false
|
||||||
const raise = { warning: () => (invalid = true) }
|
const log = { warning: () => (invalid = true) }
|
||||||
const p1 = new Point(10, 10).withRaise(raise)
|
const p1 = new Point(10, 10).__withLog(log)
|
||||||
expect(invalid).to.equal(false)
|
expect(invalid).to.equal(false)
|
||||||
try {
|
try {
|
||||||
p1.sitsRoughlyOn('a')
|
p1.sitsRoughlyOn('a')
|
||||||
|
|
|
@ -10,6 +10,7 @@ describe('Snapped options', () => {
|
||||||
it('Should snap a percentage options to equal steps', () => {
|
it('Should snap a percentage options to equal steps', () => {
|
||||||
const part = {
|
const part = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
measurements: ['head'],
|
||||||
options: {
|
options: {
|
||||||
test: { pct: 30, min: 0, max: 100, snap: 12, toAbs },
|
test: { pct: 30, min: 0, max: 100, snap: 12, toAbs },
|
||||||
},
|
},
|
||||||
|
@ -17,13 +18,13 @@ describe('Snapped options', () => {
|
||||||
const design = new Design({ parts: [part] })
|
const design = new Design({ parts: [part] })
|
||||||
const patternA = new design({ options: { test: 0.13 }, measurements }).draft()
|
const patternA = new design({ options: { test: 0.13 }, measurements }).draft()
|
||||||
const patternB = new design({ options: { test: 0.27 }, measurements }).draft()
|
const patternB = new design({ options: { test: 0.27 }, measurements }).draft()
|
||||||
expect(patternA.settings.absoluteOptions.test).to.equal(60)
|
expect(patternA.settings[0].absoluteOptions.test).to.equal(60)
|
||||||
expect(patternB.settings.absoluteOptions.test).to.equal(108)
|
expect(patternB.settings[0].absoluteOptions.test).to.equal(108)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should snap a percentage options to the Fibonacci sequence', () => {
|
it('Should snap a percentage options to the Fibonacci sequence', () => {
|
||||||
const part = {
|
const part = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
measurements: ['head'],
|
||||||
options: {
|
options: {
|
||||||
test: {
|
test: {
|
||||||
pct: 30,
|
pct: 30,
|
||||||
|
@ -38,9 +39,9 @@ describe('Snapped options', () => {
|
||||||
const patternA = new design({ options: { test: 0.13 }, measurements }).draft()
|
const patternA = new design({ options: { test: 0.13 }, measurements }).draft()
|
||||||
const patternB = new design({ options: { test: 0.27 }, measurements }).draft()
|
const patternB = new design({ options: { test: 0.27 }, measurements }).draft()
|
||||||
const patternC = new design({ options: { test: 0.97 }, measurements }).draft()
|
const patternC = new design({ options: { test: 0.97 }, measurements }).draft()
|
||||||
expect(patternA.settings.absoluteOptions.test).to.equal(55)
|
expect(patternA.settings[0].absoluteOptions.test).to.equal(55)
|
||||||
expect(patternB.settings.absoluteOptions.test).to.equal(89)
|
expect(patternB.settings[0].absoluteOptions.test).to.equal(89)
|
||||||
expect(patternC.settings.absoluteOptions.test).to.equal(388)
|
expect(patternC.settings[0].absoluteOptions.test).to.equal(388)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should snap a percentage options to imperial snaps', () => {
|
it('Should snap a percentage options to imperial snaps', () => {
|
||||||
|
@ -64,10 +65,10 @@ describe('Snapped options', () => {
|
||||||
const patternB = new design({ options: { test: 0.27 }, measurements, units: 'metric' }).draft()
|
const patternB = new design({ options: { test: 0.27 }, measurements, units: 'metric' }).draft()
|
||||||
const patternC = new design({ options: { test: 0.97 }, measurements, units: 'metric' }).draft()
|
const patternC = new design({ options: { test: 0.97 }, measurements, units: 'metric' }).draft()
|
||||||
const patternD = new design({ options: { test: 0.01 }, measurements, units: 'metric' }).draft()
|
const patternD = new design({ options: { test: 0.01 }, measurements, units: 'metric' }).draft()
|
||||||
expect(patternA.settings.absoluteOptions.test).to.equal(50)
|
expect(patternA.settings[0].absoluteOptions.test).to.equal(50)
|
||||||
expect(patternB.settings.absoluteOptions.test).to.equal(100)
|
expect(patternB.settings[0].absoluteOptions.test).to.equal(100)
|
||||||
expect(patternC.settings.absoluteOptions.test).to.equal(388)
|
expect(patternC.settings[0].absoluteOptions.test).to.equal(388)
|
||||||
expect(patternD.settings.absoluteOptions.test).to.equal(4)
|
expect(patternD.settings[0].absoluteOptions.test).to.equal(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should snap a percentage options to metrics snaps', () => {
|
it('Should snap a percentage options to metrics snaps', () => {
|
||||||
|
@ -107,9 +108,9 @@ describe('Snapped options', () => {
|
||||||
measurements,
|
measurements,
|
||||||
units: 'imperial',
|
units: 'imperial',
|
||||||
}).draft()
|
}).draft()
|
||||||
expect(patternA.settings.absoluteOptions.test).to.equal(50.8)
|
expect(patternA.settings[0].absoluteOptions.test).to.equal(50.8)
|
||||||
expect(patternB.settings.absoluteOptions.test).to.equal(101.6)
|
expect(patternB.settings[0].absoluteOptions.test).to.equal(101.6)
|
||||||
expect(patternC.settings.absoluteOptions.test).to.equal(388)
|
expect(patternC.settings[0].absoluteOptions.test).to.equal(388)
|
||||||
expect(patternD.settings.absoluteOptions.test).to.equal(4)
|
expect(patternD.settings[0].absoluteOptions.test).to.equal(4)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -69,8 +69,8 @@ describe('Store', () => {
|
||||||
const Test = new Design({ plugins: [plugin], parts: [part] })
|
const Test = new Design({ plugins: [plugin], parts: [part] })
|
||||||
const pattern = new Test()
|
const pattern = new Test()
|
||||||
pattern.draft()
|
pattern.draft()
|
||||||
expect(pattern.store.get('test.message.warning')).to.equal('hello warning')
|
expect(pattern.stores[0].get('test.message.warning')).to.equal('hello warning')
|
||||||
expect(pattern.store.get('test.message.info')).to.equal('hello info')
|
expect(pattern.stores[0].get('test.message.info')).to.equal('hello info')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should make top-level plugin methods available via shorthand', () => {
|
it('Should make top-level plugin methods available via shorthand', () => {
|
||||||
|
@ -103,7 +103,7 @@ describe('Store', () => {
|
||||||
const Test = new Design({ plugins: [plugin], parts: [part] })
|
const Test = new Design({ plugins: [plugin], parts: [part] })
|
||||||
const pattern = new Test()
|
const pattern = new Test()
|
||||||
pattern.draft()
|
pattern.draft()
|
||||||
expect(pattern.store.get('test.example_part.a')).to.equal('hello A')
|
expect(pattern.stores[0].get('test.example_part.a')).to.equal('hello A')
|
||||||
expect(pattern.store.get('test.example_part.b')).to.equal('hello B')
|
expect(pattern.stores[0].get('test.example_part.b')).to.equal('hello B')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import chai from 'chai'
|
import chai from 'chai'
|
||||||
import {
|
import {
|
||||||
Point,
|
Point,
|
||||||
isCoord,
|
|
||||||
capitalize,
|
capitalize,
|
||||||
beamsIntersect,
|
beamsIntersect,
|
||||||
linesIntersect,
|
linesIntersect,
|
||||||
|
@ -21,20 +20,14 @@ import {
|
||||||
lineIntersectsCircle,
|
lineIntersectsCircle,
|
||||||
stretchToScale,
|
stretchToScale,
|
||||||
round,
|
round,
|
||||||
sampleStyle,
|
|
||||||
deg2rad,
|
deg2rad,
|
||||||
rad2deg,
|
rad2deg,
|
||||||
pctBasedOn,
|
pctBasedOn,
|
||||||
macroName,
|
|
||||||
} from '../src/index.mjs'
|
} from '../src/index.mjs'
|
||||||
|
|
||||||
const { expect } = chai
|
const { expect } = chai
|
||||||
|
|
||||||
describe('Utils', () => {
|
describe('Utils', () => {
|
||||||
it('Should return the correct macro name', () => {
|
|
||||||
expect(macroName('test')).to.equal('_macro_test')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should find the intersection of two endless line segments', () => {
|
it('Should find the intersection of two endless line segments', () => {
|
||||||
let a = new Point(10, 20)
|
let a = new Point(10, 20)
|
||||||
let b = new Point(20, 24)
|
let b = new Point(20, 24)
|
||||||
|
@ -463,34 +456,6 @@ describe('Utils', () => {
|
||||||
expect(round(i.y)).to.equal(400)
|
expect(round(i.y)).to.equal(400)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should check for valid coordinate', () => {
|
|
||||||
expect(isCoord(23423.23)).to.equal(true)
|
|
||||||
expect(isCoord(0)).to.equal(true)
|
|
||||||
expect(isCoord()).to.equal(false)
|
|
||||||
expect(isCoord(null)).to.equal(false)
|
|
||||||
expect(isCoord('hi')).to.equal(false)
|
|
||||||
expect(isCoord(NaN)).to.equal(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should return the correct sample style', () => {
|
|
||||||
expect(sampleStyle(0, 5)).to.equal('stroke: hsl(-66, 100%, 35%);')
|
|
||||||
expect(sampleStyle(1, 5)).to.equal('stroke: hsl(0, 100%, 35%);')
|
|
||||||
expect(sampleStyle(2, 5)).to.equal('stroke: hsl(66, 100%, 35%);')
|
|
||||||
expect(sampleStyle(3, 5)).to.equal('stroke: hsl(132, 100%, 35%);')
|
|
||||||
expect(sampleStyle(4, 5)).to.equal('stroke: hsl(198, 100%, 35%);')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should return the correct sample styles', () => {
|
|
||||||
const styles = [
|
|
||||||
'stroke: red;',
|
|
||||||
'stroke: blue;',
|
|
||||||
'stroke: green;',
|
|
||||||
'stroke: pink;',
|
|
||||||
'stroke: orange;',
|
|
||||||
]
|
|
||||||
for (let i = 0; i < 5; i++) expect(sampleStyle(i, 5, styles)).to.equal(styles[i])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should convert degrees to radians', () => {
|
it('Should convert degrees to radians', () => {
|
||||||
expect(deg2rad(0)).to.equal(0)
|
expect(deg2rad(0)).to.equal(0)
|
||||||
expect(round(deg2rad(69))).to.equal(1.2)
|
expect(round(deg2rad(69))).to.equal(1.2)
|
||||||
|
|
4
sites/shared/.eslintignore
Normal file
4
sites/shared/.eslintignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
themes/*
|
||||||
|
styles/*
|
||||||
|
.eslintignore
|
||||||
|
*.css
|
18
sites/shared/.eslintrc.yml
Normal file
18
sites/shared/.eslintrc.yml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
env:
|
||||||
|
browser: true
|
||||||
|
es2021: true
|
||||||
|
extends:
|
||||||
|
- eslint:recommended
|
||||||
|
- plugin:react/recommended
|
||||||
|
overrides: []
|
||||||
|
parserOptions:
|
||||||
|
ecmaVersion: latest
|
||||||
|
sourceType: module
|
||||||
|
plugins:
|
||||||
|
- react
|
||||||
|
rules:
|
||||||
|
react/prop-types: off
|
||||||
|
react/react-in-jsx-scope: off
|
||||||
|
globals:
|
||||||
|
module: readonly
|
||||||
|
|
|
@ -1,55 +1,67 @@
|
||||||
import React from 'react';
|
import React from 'react'
|
||||||
import ResetButtons from './reset-buttons'
|
import ResetButtons from './reset-buttons'
|
||||||
import {EventGroup} from 'shared/components/workbench/events'
|
import { LogGroup } from 'shared/components/workbench/logs'
|
||||||
import DefaultErrorView from './view';
|
import DefaultErrorView from './view'
|
||||||
|
|
||||||
const ErrorView = (props) => {
|
const ErrorView = (props) => {
|
||||||
if (props.children) return props.children
|
if (props.children) return props.children
|
||||||
|
|
||||||
const inspectChildrenProps = {
|
const inspectChildrenProps = {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
events: [props.error],
|
logs: [props.error],
|
||||||
units: props.gist?.units
|
units: props.gist?.units,
|
||||||
}
|
}
|
||||||
const inspectChildren = (<EventGroup {...inspectChildrenProps}></EventGroup>)
|
const inspectChildren = <LogGroup {...inspectChildrenProps}></LogGroup>
|
||||||
return (props.children || (<DefaultErrorView inspectChildren={inspectChildren}>
|
return (
|
||||||
|
props.children || (
|
||||||
|
<DefaultErrorView inspectChildren={inspectChildren}>
|
||||||
<h4>If you think your last action caused this error, you can: </h4>
|
<h4>If you think your last action caused this error, you can: </h4>
|
||||||
<ResetButtons undoGist={props.undoGist} resetGist={props.resetGist} />
|
<ResetButtons undoGist={props.undoGist} resetGist={props.resetGist} />
|
||||||
</DefaultErrorView>))
|
</DefaultErrorView>
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ErrorBoundary extends React.Component {
|
class ErrorBoundary extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props)
|
||||||
this.state = { hasError: false };
|
this.state = { hasError: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromError(error) {
|
static getDerivedStateFromError(error) {
|
||||||
// Update state so the next render will show the fallback UI.
|
// Update state so the next render will show the fallback UI.
|
||||||
return { hasError: true, error };
|
return { hasError: true, error }
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidCatch(error, errorInfo) {
|
componentDidCatch(error, errorInfo) {
|
||||||
// You can also log the error to an error reporting service
|
// You can also log the error to an error reporting service
|
||||||
console.log(error, errorInfo);
|
console.log(error, errorInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (this.props.gist !== prevProps.gist) {
|
if (this.props.gist !== prevProps.gist) {
|
||||||
this.setState({hasError: false})
|
this.setState({ hasError: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.hasError) {
|
if (this.state.hasError) {
|
||||||
// You can render any custom fallback UI
|
// You can render any custom fallback UI
|
||||||
return <ErrorView {...this.props} error={this.state.error}>{this.errorView}</ErrorView>
|
return (
|
||||||
|
<ErrorView {...this.props} error={this.state.error}>
|
||||||
|
{this.errorView}
|
||||||
|
</ErrorView>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return this.props.children;
|
return this.props.children
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
return <ErrorView {...this.props} error={e}>{this.errorView}</ErrorView>;
|
return (
|
||||||
|
<ErrorView {...this.props} error={e}>
|
||||||
|
{this.errorView}
|
||||||
|
</ErrorView>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,30 @@
|
||||||
import DefaultErrorView from 'shared/components/error/view';
|
import DefaultErrorView from 'shared/components/error/view'
|
||||||
|
|
||||||
const Error = ({ draft, patternProps, error, updateGist }) => {
|
const Error = ({ logs=[], updateGist }) => {
|
||||||
const inspectChildren = (<ul className="list-disc list-inside ml-4 text-xl">
|
let errors = 0
|
||||||
|
let warnings = 0
|
||||||
|
for (const log of logs) {
|
||||||
|
errors += log.errors.length
|
||||||
|
warnings += log.warnings.length
|
||||||
|
}
|
||||||
|
|
||||||
|
const inspectChildren = (
|
||||||
|
<ul className="list-disc list-inside ml-4 text-xl">
|
||||||
<li>
|
<li>
|
||||||
Check the <button className="btn-link" onClick={() => updateGist(['_state', 'view'], 'events')}>
|
Check the{' '}
|
||||||
<strong>{patternProps?.events?.error?.length} errors</strong> and <strong>
|
<button className="btn-link" onClick={() => updateGist(['_state', 'view'], 'logs')}>
|
||||||
{patternProps?.events?.warning?.length} warnings</strong></button>
|
<strong>{errors.length} errors</strong> and <strong>{warnings.length} warnings</strong>
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>Check the partially rendered pattern below to see which areas are problematic</li>
|
<li>Check the partially rendered pattern below to see which areas are problematic</li>
|
||||||
</ul>)
|
</ul>
|
||||||
|
)
|
||||||
|
|
||||||
return (<DefaultErrorView inspectChildren={inspectChildren}>
|
return (
|
||||||
<p>Don't be alarmed, but we ran into some trouble while drafting this pattern.</p>
|
<DefaultErrorView inspectChildren={inspectChildren}>
|
||||||
</DefaultErrorView>)
|
<p>No need to be alarmed, but we ran into some trouble while drafting this pattern.</p>
|
||||||
|
</DefaultErrorView>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Error
|
export default Error
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import SvgWrapper from './svg-wrapper'
|
import SvgWrapper from './svg-wrapper'
|
||||||
import Error from './error.js'
|
import Error from './error.js'
|
||||||
|
|
||||||
const LabDraft = props => {
|
const LabDraft = (props) => {
|
||||||
const { app, draft, gist, updateGist, unsetGist, showInfo, feedback, hasRequiredMeasurements } = props
|
const { app, draft, gist, updateGist, unsetGist, showInfo, feedback, hasRequiredMeasurements } =
|
||||||
|
props
|
||||||
|
|
||||||
if (!draft || !hasRequiredMeasurements) return null
|
if (!draft || !hasRequiredMeasurements) return null
|
||||||
|
|
||||||
// Render as SVG
|
// Render as SVG
|
||||||
if (gist?.renderer === 'svg') {
|
if (gist?.renderer === 'svg') {
|
||||||
let svg
|
let svg
|
||||||
try { svg = draft.render() }
|
try {
|
||||||
catch(error) {
|
svg = draft.render()
|
||||||
|
} catch (error) {
|
||||||
console.log('Failed to render design', error)
|
console.log('Failed to render design', error)
|
||||||
return <Error error={error} {...props} />
|
return <Error error={error} {...props} />
|
||||||
}
|
}
|
||||||
|
@ -19,19 +21,26 @@ const LabDraft = props => {
|
||||||
|
|
||||||
// Render as React
|
// Render as React
|
||||||
let patternProps = {}
|
let patternProps = {}
|
||||||
try { patternProps = draft.getRenderProps() }
|
try {
|
||||||
catch(error) {
|
patternProps = draft.getRenderProps()
|
||||||
|
} catch (error) {
|
||||||
console.log('Failed to get render props for design', error)
|
console.log('Failed to get render props for design', error)
|
||||||
return <Error error={error} {...props} />
|
return <Error error={error} {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errors = []
|
||||||
|
for (const logs of patternProps.logs) {
|
||||||
|
errors.push(...logs.error)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{(!patternProps || patternProps.logs?.error?.length > 0)
|
{!patternProps || errors.length > 0 ? (
|
||||||
? <Error {...{ draft, patternProps, updateGist }} />
|
<Error {...{ draft, patternProps, updateGist }} />
|
||||||
: null
|
) : null}
|
||||||
}
|
<SvgWrapper
|
||||||
<SvgWrapper {...{ draft, patternProps, gist, updateGist, unsetGist, showInfo, app, feedback }} />
|
{...{ draft, patternProps, gist, updateGist, unsetGist, showInfo, app, feedback }}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ const XrayPart = props => {
|
||||||
export const PartInner = forwardRef((props, ref) => {
|
export const PartInner = forwardRef((props, ref) => {
|
||||||
const { partName, part, gist } = props
|
const { partName, part, gist } = props
|
||||||
|
|
||||||
const grid = gist.paperless ? (
|
const Grid = gist.paperless ? (
|
||||||
<rect
|
<rect
|
||||||
x={part.topLeft.x}
|
x={part.topLeft.x}
|
||||||
y={part.topLeft.y}
|
y={part.topLeft.y}
|
||||||
|
@ -100,7 +100,7 @@ export const PartInner = forwardRef((props, ref) => {
|
||||||
) : null
|
) : null
|
||||||
|
|
||||||
return (<g ref={ref}>
|
return (<g ref={ref}>
|
||||||
{grid}
|
{Grid}
|
||||||
{
|
{
|
||||||
gist._state?.xray?.enabled &&
|
gist._state?.xray?.enabled &&
|
||||||
<XrayPart {...props} />
|
<XrayPart {...props} />
|
||||||
|
@ -140,7 +140,7 @@ const Part = props => {
|
||||||
const { partName, part} = props
|
const { partName, part} = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g {...getProps(part)} id={`part-${partName}`}>
|
<g {...getProps(part)} id={`${part.context.settings.idPrefix || ''}part-${partName}`} className={part.context.settings.idPrefix || ''}>
|
||||||
<PartInner {...props}/>
|
<PartInner {...props}/>
|
||||||
</g>
|
</g>
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,10 +2,10 @@ import Part from './part'
|
||||||
import { getProps } from './utils'
|
import { getProps } from './utils'
|
||||||
|
|
||||||
const Stack = props => {
|
const Stack = props => {
|
||||||
const { stackName, stack, gist, app, updateGist, unsetGist, showInfo } = props
|
const { stack, gist, app, updateGist, unsetGist, showInfo } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g {...getProps(stack)} id={`stack-${stackName}`}>
|
<g {...getProps(stack)}>
|
||||||
{[...stack.parts].map((part) => (
|
{[...stack.parts].map((part) => (
|
||||||
<Part {...{ app, gist, updateGist, unsetGist, showInfo }}
|
<Part {...{ app, gist, updateGist, unsetGist, showInfo }}
|
||||||
key={part.name}
|
key={part.name}
|
||||||
|
|
|
@ -26,7 +26,9 @@ import Stack from './stack'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const SvgWrapper = props => {
|
const SvgWrapper = props => {
|
||||||
const { patternProps, gist, app, updateGist, unsetGist, showInfo } = props
|
const { patternProps=false, gist, app, updateGist, unsetGist, showInfo } = props
|
||||||
|
|
||||||
|
if (!patternProps) return null
|
||||||
|
|
||||||
return <SizeMe>{({ size }) => (
|
return <SizeMe>{({ size }) => (
|
||||||
<TransformWrapper
|
<TransformWrapper
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
import Markdown from 'react-markdown'
|
|
||||||
import { formatMm } from 'shared/utils'
|
|
||||||
|
|
||||||
export const Error = ({err}) => (
|
|
||||||
<pre>
|
|
||||||
{err.stack.split(/\n/g).slice(0, 5).map((l, i) => (<code key={`error-${i}`} className={'block whitespace-pre-wrap' + (i > 0 ? ' break-all' : '')}>{l}</code>))}
|
|
||||||
</pre>
|
|
||||||
)
|
|
||||||
|
|
||||||
// Markdown wrapper to suppress creation of P tags
|
|
||||||
const Md = ({ children }) => <Markdown components={{ p: props => props.children }}>{children}</Markdown>
|
|
||||||
|
|
||||||
const Event = ({ evt, units }) => {
|
|
||||||
if (Array.isArray(evt)) {
|
|
||||||
if (evt[1]?.mm) return <span dangerouslySetInnerHTML={{
|
|
||||||
__html: `${evt[0]}: <strong>${formatMm(evt[1].mm, units, 'html')}</strong>`
|
|
||||||
}}/>
|
|
||||||
else return evt.map(e => <Event evt={e} key={e} />)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (evt.message) return <Error err={evt} />
|
|
||||||
else if (typeof evt === 'string') return <Md>{evt}</Md>
|
|
||||||
|
|
||||||
return <Md>Note a recognized event: {JSON.stringify(evt, null ,2)}</Md>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EventGroup = ({ type='info', events=[], units='metric' }) => events.length > 0 ? (
|
|
||||||
<div className="">
|
|
||||||
<h3 className="capitalize" id={`events-${type}`}>{type}</h3>
|
|
||||||
<table className="table w-full mdx">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th className="text-right w-16">#</th>
|
|
||||||
<th>Message</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{events.map((evt, i) => (
|
|
||||||
<tr key={i} className="leading-1 hover:bg-base-200 hover:bg-opacity-40">
|
|
||||||
<td className="text-right p-1 pr-4 font-bold opacity-80 text-accent">{i}</td>
|
|
||||||
<td className="p-1 pl-4"><Event evt={evt} units={units}/></td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
) : null
|
|
||||||
|
|
||||||
const order = [
|
|
||||||
'error',
|
|
||||||
'warning',
|
|
||||||
'info',
|
|
||||||
'debug'
|
|
||||||
]
|
|
||||||
const Events = props => props?.draft?.store.logs
|
|
||||||
? (
|
|
||||||
<div className="max-w-screen-xl m-auto">
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<ul className="flex flex-row row-wrap">
|
|
||||||
{order.map(type => (props.draft.store.logs[type].length > 0)
|
|
||||||
? (
|
|
||||||
<li key={type} className="">
|
|
||||||
<a href={`#events-${type}`} className={`text-secondary font-bold capitalize text-xl`}>{type}</a>
|
|
||||||
{type === 'debug' ? '' : <span className="px-2 font-bold">|</span>}
|
|
||||||
</li>
|
|
||||||
) : (
|
|
||||||
<li key={type} className="text-base-content font-bold capitalize text-xl">
|
|
||||||
<span className="opacity-50">{type}</span>
|
|
||||||
{type === 'debug' ? '' : <span className="px-2 font-bold">|</span>}
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
{order.map(type => <EventGroup type={type} events={props.draft.store.logs[type]} units={props.gist.units}/>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null
|
|
||||||
|
|
||||||
export default Events
|
|
112
sites/shared/components/workbench/logs.js
Normal file
112
sites/shared/components/workbench/logs.js
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
import Markdown from 'react-markdown'
|
||||||
|
import { formatMm } from 'shared/utils'
|
||||||
|
import { Tab, Tabs } from '../mdx/tabs.js'
|
||||||
|
|
||||||
|
export const Error = ({ err }) => (
|
||||||
|
<pre>
|
||||||
|
{err.stack
|
||||||
|
.split(/\n/g)
|
||||||
|
.slice(0, 5)
|
||||||
|
.map((l, i) => (
|
||||||
|
<code
|
||||||
|
key={`error-${i}`}
|
||||||
|
className={'block whitespace-pre-wrap' + (i > 0 ? ' break-all' : '')}
|
||||||
|
>
|
||||||
|
{l}
|
||||||
|
</code>
|
||||||
|
))}
|
||||||
|
</pre>
|
||||||
|
)
|
||||||
|
|
||||||
|
// Markdown wrapper to suppress creation of P tags
|
||||||
|
const Md = ({ children }) => (
|
||||||
|
<Markdown components={{ p: (props) => props.children }}>{children}</Markdown>
|
||||||
|
)
|
||||||
|
|
||||||
|
const Log = ({ log, units }) => {
|
||||||
|
if (Array.isArray(log)) {
|
||||||
|
if (log[1]?.mm)
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `${log[0]}: <strong>${formatMm(log[1].mm, units, 'html')}</strong>`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
else return log.map((l) => <Log log={l} key={l} />)
|
||||||
|
} else if (log.message) return <Error err={log} />
|
||||||
|
else if (typeof log === 'string') return <Md>{log}</Md>
|
||||||
|
|
||||||
|
return <Md>Unrecognized log: {JSON.stringify(log, null, 2)}</Md>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LogGroup = ({ type = 'info', logs = [], units = 'metric' }) =>
|
||||||
|
logs.length > 0 ? (
|
||||||
|
<div className="">
|
||||||
|
<h3 className="capitalize" id={`logs-${type}`}>
|
||||||
|
{type}
|
||||||
|
</h3>
|
||||||
|
<table className="table w-full mdx">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="text-right w-16">#</th>
|
||||||
|
<th>Message</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{logs.map((log, i) => (
|
||||||
|
<tr key={i} className="leading-1 hover:bg-base-200 hover:bg-opacity-40">
|
||||||
|
<td className="text-right p-1 pr-4 font-bold opacity-80 text-accent">{i}</td>
|
||||||
|
<td className="p-1 pl-4">
|
||||||
|
<Log log={log} units={units} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
|
||||||
|
const order = ['error', 'warning', 'info', 'debug']
|
||||||
|
|
||||||
|
const StoreLogs = ({ logs, units }) => (
|
||||||
|
<div className="max-w-screen-xl m-auto">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<ul className="flex flex-row row-wrap">
|
||||||
|
{order.map((type) =>
|
||||||
|
logs[type].length > 0 ? (
|
||||||
|
<li key={type} className="">
|
||||||
|
<a href={`#logs-${type}`} className={`text-secondary font-bold capitalize text-xl`}>
|
||||||
|
{type}
|
||||||
|
</a>
|
||||||
|
{type === 'debug' ? '' : <span className="px-2 font-bold">|</span>}
|
||||||
|
</li>
|
||||||
|
) : (
|
||||||
|
<li key={type} className="text-base-content font-bold capitalize text-xl">
|
||||||
|
<span className="opacity-50">{type}</span>
|
||||||
|
{type === 'debug' ? '' : <span className="px-2 font-bold">|</span>}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
{order.map((type) => (
|
||||||
|
<LogGroup type={type} logs={logs[type]} units={units} key={type} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const Logs = (props) =>
|
||||||
|
props.draft.stores.length === 1 ? (
|
||||||
|
<StoreLogs logs={props.draft.stores[0].logs} units={props.gist.units} />
|
||||||
|
) : (
|
||||||
|
<Tabs tabs={props.draft.stores.map((store, i) => `Set ${i}`).join(',')}>
|
||||||
|
{props.draft.stores.map((store, i) => (
|
||||||
|
<Tab key={i}>
|
||||||
|
<StoreLogs logs={store.logs} units={props.gist.units} />
|
||||||
|
</Tab>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Logs
|
|
@ -1,90 +1,92 @@
|
||||||
import MenuIcon from 'shared/components/icons/menu.js'
|
import MenuIcon from 'shared/components/icons/menu.js'
|
||||||
import { linkClasses, Chevron } from 'shared/components/navigation/primary.js'
|
import { linkClasses, Chevron } from 'shared/components/navigation/primary.js'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import {defaultGist} from 'shared/hooks/useGist'
|
import { defaultGist } from 'shared/hooks/useGist'
|
||||||
|
|
||||||
const View = props => {
|
const View = (props) => {
|
||||||
const { t } = useTranslation(['app'])
|
const { t } = useTranslation(['app'])
|
||||||
const entries = [
|
const entries = [
|
||||||
{
|
{
|
||||||
name: 'measurements',
|
name: 'measurements',
|
||||||
title: t('measurements'),
|
title: t('measurements'),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'measurements', true)
|
onClick: () => props.updateGist(['_state', 'view'], 'measurements', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'draft',
|
name: 'draft',
|
||||||
title: t('draftDesign', { design: props.design.config.data.name }),
|
title: t('draftDesign', { design: props.design.config.data.name }),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'draft', true)
|
onClick: () => props.updateGist(['_state', 'view'], 'draft', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'test',
|
name: 'test',
|
||||||
title: t('testDesign', { design: props.design.config.data.name }),
|
title: t('testDesign', { design: props.design.config.data.name }),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'test', true)
|
onClick: () => props.updateGist(['_state', 'view'], 'test', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'printingLayout',
|
name: 'printingLayout',
|
||||||
title: t('layoutThing', { thing: props.design.config.data.name })
|
title: t('layoutThing', { thing: props.design.config.data.name }) + ': ' + t('forPrinting'),
|
||||||
+ ': ' + t('forPrinting'),
|
onClick: () => props.updateGist(['_state', 'view'], 'printingLayout', true),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'printingLayout', true)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cuttingLayout',
|
name: 'cuttingLayout',
|
||||||
title: t('layoutThing', { thing: props.design.config.data.name })
|
title: t('layoutThing', { thing: props.design.config.data.name }) + ': ' + t('forCutting'),
|
||||||
+ ': ' + t('forCutting'),
|
onClick: () => props.updateGist(['_state', 'view'], 'cuttingLayout', true),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'cuttingLayout', true)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'export',
|
name: 'export',
|
||||||
title: t('exportThing', { thing: props.design.config.data.name }),
|
title: t('exportThing', { thing: props.design.config.data.name }),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'export', true)
|
onClick: () => props.updateGist(['_state', 'view'], 'export', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'events',
|
name: 'logs',
|
||||||
title: t('events'),
|
title: t('logs'),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'events', true)
|
onClick: () => props.updateGist(['_state', 'view'], 'logs', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'yaml',
|
name: 'yaml',
|
||||||
title: t('YAML'),
|
title: t('YAML'),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'yaml', true)
|
onClick: () => props.updateGist(['_state', 'view'], 'yaml', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'json',
|
name: 'json',
|
||||||
title: t('JSON'),
|
title: t('JSON'),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'json', true)
|
onClick: () => props.updateGist(['_state', 'view'], 'json', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'edit',
|
name: 'edit',
|
||||||
title: t('editThing', { thing: 'YAML' }),
|
title: t('editThing', { thing: 'YAML' }),
|
||||||
onClick: () => props.updateGist(['_state', 'view'], 'edit', true)
|
onClick: () => props.updateGist(['_state', 'view'], 'edit', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'clear',
|
name: 'clear',
|
||||||
title: t('clearThing', { thing: 'YAML' }),
|
title: t('clearThing', { thing: 'YAML' }),
|
||||||
onClick: () => props.setGist(defaultGist(props.design, props.gist.locale))
|
onClick: () => props.setGist(defaultGist(props.design, props.gist.locale)),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<details className='py-1' open>
|
<details className="py-1" open>
|
||||||
<summary className={`
|
<summary
|
||||||
|
className={`
|
||||||
flex flex-row uppercase gap-4 font-bold text-lg
|
flex flex-row uppercase gap-4 font-bold text-lg
|
||||||
hover:cursor-row-resize
|
hover:cursor-row-resize
|
||||||
p-2
|
p-2
|
||||||
text-base-content
|
text-base-content
|
||||||
sm:text-base-content
|
sm:text-base-content
|
||||||
items-center
|
items-center
|
||||||
`}>
|
`}
|
||||||
<span className="text-secondary-focus mr-4"><MenuIcon /></span>
|
>
|
||||||
<span className={`grow ${linkClasses} hover:cursor-resize`}>
|
<span className="text-secondary-focus mr-4">
|
||||||
{t('view')}
|
<MenuIcon />
|
||||||
</span>
|
</span>
|
||||||
|
<span className={`grow ${linkClasses} hover:cursor-resize`}>{t('view')}</span>
|
||||||
<Chevron />
|
<Chevron />
|
||||||
</summary>
|
</summary>
|
||||||
<ul className="pl-5 list-inside">
|
<ul className="pl-5 list-inside">
|
||||||
{entries.map(entry => (
|
{entries.map((entry) => (
|
||||||
<li key={entry.title} className="flex flex-row">
|
<li key={entry.title} className="flex flex-row">
|
||||||
<button title={entry.title} className={`
|
<button
|
||||||
|
title={entry.title}
|
||||||
|
className={`
|
||||||
grow pl-2 border-l-2
|
grow pl-2 border-l-2
|
||||||
${linkClasses}
|
${linkClasses}
|
||||||
hover:cursor-pointer
|
hover:cursor-pointer
|
||||||
|
@ -92,22 +94,28 @@ const View = props => {
|
||||||
sm:hover:border-secondary-focus
|
sm:hover:border-secondary-focus
|
||||||
text-left
|
text-left
|
||||||
capitalize
|
capitalize
|
||||||
${entry.name === props.gist?._state?.view
|
${
|
||||||
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
|
entry.name === props.gist?._state?.view
|
||||||
: 'text-base-content sm:text-base-content'
|
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
|
||||||
|
: 'text-base-content sm:text-base-content'
|
||||||
}
|
}
|
||||||
`} onClick={entry.onClick}>
|
`}
|
||||||
<span className={`
|
onClick={entry.onClick}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
text-3xl mr-2 inline-block p-0 leading-3
|
text-3xl mr-2 inline-block p-0 leading-3
|
||||||
${entry.name === props.gist?._state?.view
|
${
|
||||||
? 'text-secondary sm:text-secondary-focus translate-y-1 font-bold'
|
entry.name === props.gist?._state?.view
|
||||||
: 'translate-y-3'
|
? 'text-secondary sm:text-secondary-focus translate-y-1 font-bold'
|
||||||
|
: 'translate-y-3'
|
||||||
}
|
}
|
||||||
`}>
|
`}
|
||||||
|
>
|
||||||
{entry.name === props.gist?._state?.view ? <>•</> : <>°</>}
|
{entry.name === props.gist?._state?.view ? <>•</> : <>°</>}
|
||||||
</span>
|
</span>
|
||||||
<span className={entry.name === props.gist?._state?.view ? 'font-bold' : ''}>
|
<span className={entry.name === props.gist?._state?.view ? 'font-bold' : ''}>
|
||||||
{ entry.title }
|
{entry.title}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
|
import SvgWrapper from './draft/svg-wrapper'
|
||||||
|
import Error from './draft/error.js'
|
||||||
import { svgattrPlugin } from '@freesewing/plugin-svgattr'
|
import { svgattrPlugin } from '@freesewing/plugin-svgattr'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
const LabSample = ({ gist, draft }) => {
|
const LabSample = ({ gist, draft, updateGist, unsetGist, showInfo, app, feedback }) => {
|
||||||
|
|
||||||
const { t } = useTranslation(['workbench'])
|
const { t } = useTranslation(['workbench'])
|
||||||
let svg
|
let svg
|
||||||
let title = ''
|
let title = ''
|
||||||
|
let patternProps
|
||||||
|
const errors = []
|
||||||
if (gist.sample) {
|
if (gist.sample) {
|
||||||
try {
|
try {
|
||||||
draft.use(svgattrPlugin, {
|
draft.use(svgattrPlugin, {
|
||||||
class: 'freesewing pattern max-h-screen'
|
class: 'freesewing pattern max-h-screen'
|
||||||
})
|
})
|
||||||
draft.sample()
|
draft = draft.sample()
|
||||||
svg = draft.render()
|
// Render as React
|
||||||
|
patternProps = draft.getRenderProps()
|
||||||
|
for (const logs of patternProps.logs) errors.push(...logs.error)
|
||||||
}
|
}
|
||||||
catch(err) {
|
catch(err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
@ -27,6 +33,12 @@ const LabSample = ({ gist, draft }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
|
{!patternProps || errors.length > 0 ? (
|
||||||
|
<Error {...{ draft, patternProps, updateGist }} />
|
||||||
|
) : null}
|
||||||
|
<SvgWrapper
|
||||||
|
{...{ draft, patternProps, gist, updateGist, unsetGist, showInfo, app, feedback }}
|
||||||
|
/>
|
||||||
<div className="freesewing pattern" dangerouslySetInnerHTML={{__html: svg}} />
|
<div className="freesewing pattern" dangerouslySetInnerHTML={{__html: svg}} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useEffect, useState, useMemo,} from 'react'
|
import { useEffect, useState, useMemo } from 'react'
|
||||||
import {useGist} from 'shared/hooks/useGist'
|
import { useGist } from 'shared/hooks/useGist'
|
||||||
import Layout from 'shared/components/layouts/default'
|
import Layout from 'shared/components/layouts/default'
|
||||||
import Menu from 'shared/components/workbench/menu/index.js'
|
import Menu from 'shared/components/workbench/menu/index.js'
|
||||||
import DraftError from 'shared/components/workbench/draft/error.js'
|
import DraftError from 'shared/components/workbench/draft/error.js'
|
||||||
|
@ -14,11 +14,11 @@ import LabSample from 'shared/components/workbench/sample.js'
|
||||||
import ExportDraft from 'shared/components/workbench/exporting/index.js'
|
import ExportDraft from 'shared/components/workbench/exporting/index.js'
|
||||||
import GistAsJson from 'shared/components/workbench/gist-as-json.js'
|
import GistAsJson from 'shared/components/workbench/gist-as-json.js'
|
||||||
import GistAsYaml from 'shared/components/workbench/yaml.js'
|
import GistAsYaml from 'shared/components/workbench/yaml.js'
|
||||||
import DraftEvents from 'shared/components/workbench/events.js'
|
import DraftLogs from 'shared/components/workbench/logs.js'
|
||||||
import CutLayout from 'shared/components/workbench/layout/cut'
|
import CutLayout from 'shared/components/workbench/layout/cut'
|
||||||
import PrintingLayout from 'shared/components/workbench/layout/print'
|
import PrintingLayout from 'shared/components/workbench/layout/print'
|
||||||
|
|
||||||
import ErrorBoundary from 'shared/components/error/error-boundary';
|
import ErrorBoundary from 'shared/components/error/error-boundary'
|
||||||
|
|
||||||
const views = {
|
const views = {
|
||||||
measurements: Measurements,
|
measurements: Measurements,
|
||||||
|
@ -27,7 +27,7 @@ const views = {
|
||||||
printingLayout: PrintingLayout,
|
printingLayout: PrintingLayout,
|
||||||
cuttingLayout: CutLayout,
|
cuttingLayout: CutLayout,
|
||||||
export: ExportDraft,
|
export: ExportDraft,
|
||||||
events: DraftEvents,
|
logs: DraftLogs,
|
||||||
yaml: GistAsYaml,
|
yaml: GistAsYaml,
|
||||||
json: GistAsJson,
|
json: GistAsJson,
|
||||||
welcome: () => <p>TODO</p>,
|
welcome: () => <p>TODO</p>,
|
||||||
|
@ -54,15 +54,16 @@ const doPreload = async (preload, from, design, gist, setGist, setPreloaded) =>
|
||||||
* keeping the gist state, which will trickle down
|
* keeping the gist state, which will trickle down
|
||||||
* to all workbench subcomponents
|
* to all workbench subcomponents
|
||||||
*/
|
*/
|
||||||
const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false }) => {
|
const WorkbenchWrapper = ({ app, design, preload = false, from = false, layout = false }) => {
|
||||||
|
|
||||||
// State for gist
|
// State for gist
|
||||||
const {gist, setGist, unsetGist, updateGist, gistReady, undoGist, resetGist} = useGist(design, app);
|
const { gist, setGist, unsetGist, updateGist, gistReady, undoGist, resetGist } = useGist(
|
||||||
|
design,
|
||||||
|
app
|
||||||
|
)
|
||||||
const [messages, setMessages] = useState([])
|
const [messages, setMessages] = useState([])
|
||||||
const [popup, setPopup] = useState(false)
|
const [popup, setPopup] = useState(false)
|
||||||
const [preloaded, setPreloaded] = useState(false)
|
const [preloaded, setPreloaded] = useState(false)
|
||||||
|
|
||||||
|
|
||||||
// We'll use this in more than one location
|
// We'll use this in more than one location
|
||||||
const hasRequiredMeasurements = hasRequiredMeasurementsMethod(design, gist)
|
const hasRequiredMeasurements = hasRequiredMeasurementsMethod(design, gist)
|
||||||
|
|
||||||
|
@ -70,34 +71,31 @@ const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false
|
||||||
// force view to measurements
|
// force view to measurements
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gistReady) return
|
if (!gistReady) return
|
||||||
if (gist._state?.view !== 'measurements'
|
if (gist._state?.view !== 'measurements' && !hasRequiredMeasurements)
|
||||||
&& !hasRequiredMeasurements
|
updateGist(['_state', 'view'], 'measurements')
|
||||||
) updateGist(['_state', 'view'], 'measurements')
|
|
||||||
}, [gistReady, gist._state?.view, hasRequiredMeasurements])
|
}, [gistReady, gist._state?.view, hasRequiredMeasurements])
|
||||||
|
|
||||||
// If we need to preload the gist, do so
|
// If we need to preload the gist, do so
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (preload && preload !== preloaded && from && preloaders[from]) {
|
||||||
preload &&
|
doPreload(preload, from, design, gist, setGist, setPreloaded)
|
||||||
preload !== preloaded &&
|
|
||||||
from &&
|
|
||||||
preloaders[from]
|
|
||||||
) {
|
|
||||||
doPreload(preload, from, design, gist, setGist, setPreloaded)
|
|
||||||
}
|
}
|
||||||
}, [preload, preloaded, from, design])
|
}, [preload, preloaded, from, design])
|
||||||
|
|
||||||
|
|
||||||
// Helper methods to manage the gist state
|
// Helper methods to manage the gist state
|
||||||
const updateWBGist = useMemo(() => (path, value, closeNav=false, addToHistory=true) => {
|
const updateWBGist = useMemo(
|
||||||
updateGist(path, value, addToHistory)
|
() =>
|
||||||
// Force close of menu on mobile if it is open
|
(path, value, closeNav = false, addToHistory = true) => {
|
||||||
if (closeNav && app.primaryMenu) app.setPrimaryMenu(false)
|
updateGist(path, value, addToHistory)
|
||||||
}, [app])
|
// Force close of menu on mobile if it is open
|
||||||
|
if (closeNav && app.primaryMenu) app.setPrimaryMenu(false)
|
||||||
|
},
|
||||||
|
[app]
|
||||||
|
)
|
||||||
|
|
||||||
// Helper methods to handle messages
|
// Helper methods to handle messages
|
||||||
const feedback = {
|
const feedback = {
|
||||||
add: msg => {
|
add: (msg) => {
|
||||||
const newMsgs = [...messages]
|
const newMsgs = [...messages]
|
||||||
if (Array.isArray(msg)) newMsgs.push(...msg)
|
if (Array.isArray(msg)) newMsgs.push(...msg)
|
||||||
else newMsgs.push(msg)
|
else newMsgs.push(msg)
|
||||||
|
@ -108,16 +106,18 @@ const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't do anything until the gist is ready
|
// don't do anything until the gist is ready
|
||||||
if (!gistReady) {return null}
|
if (!gistReady) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the draft here so we can pass it down to both the view and the options menu
|
// Generate the draft here so we can pass it down to both the view and the options menu
|
||||||
let draft = false
|
let draft = false
|
||||||
if (['draft', 'events', 'test', 'printingLayout'].indexOf(gist._state?.view) !== -1) {
|
if (['draft', 'logs', 'test', 'printingLayout'].indexOf(gist._state?.view) !== -1) {
|
||||||
gist.embed = true
|
gist.embed = true
|
||||||
// get the appropriate layout for the view
|
// get the appropriate layout for the view
|
||||||
const layout = gist.layouts?.[gist._state.view] || gist.layout || true
|
const layout = gist.layouts?.[gist._state.view] || gist.layout || true
|
||||||
// hand it separately to the design
|
// hand it separately to the design
|
||||||
draft = new design({...gist, layout})
|
draft = new design({ ...gist, layout })
|
||||||
draft.init()
|
draft.init()
|
||||||
|
|
||||||
// add theme to svg renderer
|
// add theme to svg renderer
|
||||||
|
@ -125,9 +125,8 @@ const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false
|
||||||
|
|
||||||
// draft it for draft and event views. Other views may add plugins, etc and we don't want to draft twice
|
// draft it for draft and event views. Other views may add plugins, etc and we don't want to draft twice
|
||||||
try {
|
try {
|
||||||
if (['draft', 'events'].indexOf(gist._state.view) > -1) draft.draft()
|
if (['draft', 'logs'].indexOf(gist._state.view) > -1) draft.draft()
|
||||||
}
|
} catch (error) {
|
||||||
catch(error) {
|
|
||||||
console.log('Failed to draft design', error)
|
console.log('Failed to draft design', error)
|
||||||
return <DraftError error={error} app={app} draft={draft} at={'draft'} />
|
return <DraftError error={error} app={app} draft={draft} at={'draft'} />
|
||||||
}
|
}
|
||||||
|
@ -152,33 +151,30 @@ const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false
|
||||||
app: app,
|
app: app,
|
||||||
noSearch: true,
|
noSearch: true,
|
||||||
workbench: true,
|
workbench: true,
|
||||||
AltMenu: <Menu {...componentProps }/>,
|
AltMenu: <Menu {...componentProps} />,
|
||||||
showInfo: setPopup,
|
showInfo: setPopup,
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorProps = {
|
const errorProps = {
|
||||||
undoGist,
|
undoGist,
|
||||||
resetGist,
|
resetGist,
|
||||||
gist
|
gist,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout to use
|
// Layout to use
|
||||||
const LayoutComponent = layout
|
const LayoutComponent = layout ? layout : Layout
|
||||||
? layout
|
|
||||||
: Layout
|
|
||||||
|
|
||||||
const Component = views[gist._state?.view]
|
const Component = views[gist._state?.view] ? views[gist._state.view] : views.welcome
|
||||||
? views[gist._state.view]
|
|
||||||
: views.welcome
|
|
||||||
|
|
||||||
return <LayoutComponent {...layoutProps}>
|
return (
|
||||||
{messages}
|
<LayoutComponent {...layoutProps}>
|
||||||
<ErrorBoundary {...errorProps}>
|
{messages}
|
||||||
<Component {...componentProps} />
|
<ErrorBoundary {...errorProps}>
|
||||||
{popup && <Modal cancel={() => setPopup(false)}>{popup}</Modal>}
|
<Component {...componentProps} />
|
||||||
</ErrorBoundary>
|
{popup && <Modal cancel={() => setPopup(false)}>{popup}</Modal>}
|
||||||
</LayoutComponent>
|
</ErrorBoundary>
|
||||||
|
</LayoutComponent>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WorkbenchWrapper
|
export default WorkbenchWrapper
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Can't seem to make this work as ESM
|
// Can't seem to make this work as ESM
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: ['tailwindcss/nesting', 'tailwindcss', 'autoprefixer'],
|
plugins: ['tailwindcss/nesting', 'tailwindcss', 'autoprefixer', 'postcss-for'],
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"lodash.unset": "^4.5.2",
|
"lodash.unset": "^4.5.2",
|
||||||
"mdast-util-toc": "^6.1.0",
|
"mdast-util-toc": "^6.1.0",
|
||||||
"pdfkit": "^0.13.0",
|
"pdfkit": "^0.13.0",
|
||||||
|
"postcss-for": "^2.1.1",
|
||||||
"react-markdown": "^8.0.0",
|
"react-markdown": "^8.0.0",
|
||||||
"react-sizeme": "^3.0.2",
|
"react-sizeme": "^3.0.2",
|
||||||
"react-timeago": "^7.1.0",
|
"react-timeago": "^7.1.0",
|
||||||
|
@ -44,6 +45,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.0",
|
"autoprefixer": "^10.4.0",
|
||||||
|
"eslint": "^8.23.1",
|
||||||
|
"eslint-plugin-react": "^7.31.8",
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"postcss": "^8.4.4",
|
"postcss": "^8.4.4",
|
||||||
"tailwindcss": "^3.0.1",
|
"tailwindcss": "^3.0.1",
|
||||||
|
|
|
@ -161,6 +161,20 @@ svg.freesewing.pattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Styling for v3 sampling */
|
||||||
|
@for $i from 1 to 10 {
|
||||||
|
|
||||||
|
svg.freesewing.pattern g.sample-$i path.fabric,
|
||||||
|
svg.freesewing.pattern g.sample-$i path.lining,
|
||||||
|
svg.freesewing.pattern g.sample-$i path.interfacing {
|
||||||
|
stroke: var(--pattern-sample-$i);
|
||||||
|
fill: var(--pattern-sample-$i);
|
||||||
|
fill-opacity: 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Styling the shadow DOM is hard to do
|
/* Styling the shadow DOM is hard to do
|
||||||
* This is for styling FreeSewing snippets
|
* This is for styling FreeSewing snippets
|
||||||
* TODO: Update snippets to use inline styles with CSS vars
|
* TODO: Update snippets to use inline styles with CSS vars
|
||||||
|
@ -257,3 +271,6 @@ figure.develop.example div.develop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -257,4 +257,17 @@ module.exports = {
|
||||||
'--pattern-stroke-6xl': "16px",
|
'--pattern-stroke-6xl': "16px",
|
||||||
// Pattern 7xl stroke width
|
// Pattern 7xl stroke width
|
||||||
'--pattern-stroke-7xl': "20px",
|
'--pattern-stroke-7xl': "20px",
|
||||||
|
|
||||||
|
// Pattern sampling styles
|
||||||
|
'--pattern-sample-1': colors.red["500"],
|
||||||
|
'--pattern-sample-2': colors.orange["500"],
|
||||||
|
'--pattern-sample-3': colors.yellow["500"],
|
||||||
|
'--pattern-sample-4': colors.lime["500"],
|
||||||
|
'--pattern-sample-5': colors.emerald["500"],
|
||||||
|
'--pattern-sample-6': colors.cyan["500"],
|
||||||
|
'--pattern-sample-7': colors.blue["500"],
|
||||||
|
'--pattern-sample-8': colors.violet["500"],
|
||||||
|
'--pattern-sample-9': colors.fuchsia["500"],
|
||||||
|
'--pattern-sample-10': colors.rose["500"],
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
187
yarn.lock
187
yarn.lock
|
@ -378,6 +378,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539"
|
||||||
integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==
|
integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==
|
||||||
|
|
||||||
|
"@babel/parser@^7.9.4":
|
||||||
|
version "7.19.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c"
|
||||||
|
integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==
|
||||||
|
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2"
|
||||||
|
@ -3481,6 +3486,19 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/linkify-it@*":
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9"
|
||||||
|
integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==
|
||||||
|
|
||||||
|
"@types/markdown-it@^12.2.3":
|
||||||
|
version "12.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51"
|
||||||
|
integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/linkify-it" "*"
|
||||||
|
"@types/mdurl" "*"
|
||||||
|
|
||||||
"@types/mdast@^3.0.0":
|
"@types/mdast@^3.0.0":
|
||||||
version "3.0.10"
|
version "3.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"
|
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"
|
||||||
|
@ -3488,7 +3506,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/unist" "*"
|
"@types/unist" "*"
|
||||||
|
|
||||||
"@types/mdurl@^1.0.0":
|
"@types/mdurl@*", "@types/mdurl@^1.0.0":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
|
resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
|
||||||
integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
|
integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
|
||||||
|
@ -4915,7 +4933,7 @@ bl@^5.0.0:
|
||||||
inherits "^2.0.4"
|
inherits "^2.0.4"
|
||||||
readable-stream "^3.4.0"
|
readable-stream "^3.4.0"
|
||||||
|
|
||||||
bluebird@^3.5.5:
|
bluebird@^3.5.5, bluebird@^3.7.2:
|
||||||
version "3.7.2"
|
version "3.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
||||||
|
@ -5450,6 +5468,13 @@ capture-stack-trace@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
|
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
|
||||||
integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==
|
integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==
|
||||||
|
|
||||||
|
catharsis@^0.9.0:
|
||||||
|
version "0.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.9.0.tgz#40382a168be0e6da308c277d3a2b3eb40c7d2121"
|
||||||
|
integrity sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==
|
||||||
|
dependencies:
|
||||||
|
lodash "^4.17.15"
|
||||||
|
|
||||||
cbor@^8.1.0:
|
cbor@^8.1.0:
|
||||||
version "8.1.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5"
|
resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5"
|
||||||
|
@ -7502,6 +7527,11 @@ entities@^4.2.0, entities@^4.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4"
|
||||||
integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==
|
integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==
|
||||||
|
|
||||||
|
entities@~2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
|
||||||
|
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
|
||||||
|
|
||||||
env-paths@^2.2.0:
|
env-paths@^2.2.0:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||||
|
@ -7941,6 +7971,26 @@ eslint-plugin-react@^7.28.0, eslint-plugin-react@^7.29.4:
|
||||||
semver "^6.3.0"
|
semver "^6.3.0"
|
||||||
string.prototype.matchall "^4.0.7"
|
string.prototype.matchall "^4.0.7"
|
||||||
|
|
||||||
|
eslint-plugin-react@^7.31.8:
|
||||||
|
version "7.31.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz#3a4f80c10be1bcbc8197be9e8b641b2a3ef219bf"
|
||||||
|
integrity sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw==
|
||||||
|
dependencies:
|
||||||
|
array-includes "^3.1.5"
|
||||||
|
array.prototype.flatmap "^1.3.0"
|
||||||
|
doctrine "^2.1.0"
|
||||||
|
estraverse "^5.3.0"
|
||||||
|
jsx-ast-utils "^2.4.1 || ^3.0.0"
|
||||||
|
minimatch "^3.1.2"
|
||||||
|
object.entries "^1.1.5"
|
||||||
|
object.fromentries "^2.0.5"
|
||||||
|
object.hasown "^1.1.1"
|
||||||
|
object.values "^1.1.5"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
resolve "^2.0.0-next.3"
|
||||||
|
semver "^6.3.0"
|
||||||
|
string.prototype.matchall "^4.0.7"
|
||||||
|
|
||||||
eslint-plugin-yaml@^0.5.0:
|
eslint-plugin-yaml@^0.5.0:
|
||||||
version "0.5.0"
|
version "0.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-yaml/-/eslint-plugin-yaml-0.5.0.tgz#8c79d9d6389b67cbcf58ef6f970c4c086665a63a"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-yaml/-/eslint-plugin-yaml-0.5.0.tgz#8c79d9d6389b67cbcf58ef6f970c4c086665a63a"
|
||||||
|
@ -9506,7 +9556,7 @@ got@^9.6.0:
|
||||||
to-readable-stream "^1.0.0"
|
to-readable-stream "^1.0.0"
|
||||||
url-parse-lax "^3.0.0"
|
url-parse-lax "^3.0.0"
|
||||||
|
|
||||||
graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
|
graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
|
||||||
version "4.2.10"
|
version "4.2.10"
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
||||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||||
|
@ -9562,6 +9612,11 @@ has-bigints@^1.0.1, has-bigints@^1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
||||||
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
|
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
|
||||||
|
|
||||||
|
has-flag@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
|
||||||
|
integrity sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==
|
||||||
|
|
||||||
has-flag@^3.0.0:
|
has-flag@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||||
|
@ -11131,6 +11186,11 @@ jest-validate@^27.3.1, jest-validate@^27.4.2:
|
||||||
leven "^3.1.0"
|
leven "^3.1.0"
|
||||||
pretty-format "^27.5.1"
|
pretty-format "^27.5.1"
|
||||||
|
|
||||||
|
js-base64@^2.1.9:
|
||||||
|
version "2.6.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
|
||||||
|
integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
|
||||||
|
|
||||||
js-sdsl@^4.1.4:
|
js-sdsl@^4.1.4:
|
||||||
version "4.1.4"
|
version "4.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.4.tgz#78793c90f80e8430b7d8dc94515b6c77d98a26a6"
|
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.4.tgz#78793c90f80e8430b7d8dc94515b6c77d98a26a6"
|
||||||
|
@ -11168,6 +11228,34 @@ js-yaml@4.1.0, js-yaml@^4.0.0, js-yaml@^4.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
argparse "^2.0.1"
|
argparse "^2.0.1"
|
||||||
|
|
||||||
|
js2xmlparser@^4.0.2:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-4.0.2.tgz#2a1fdf01e90585ef2ae872a01bc169c6a8d5e60a"
|
||||||
|
integrity sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==
|
||||||
|
dependencies:
|
||||||
|
xmlcreate "^2.0.4"
|
||||||
|
|
||||||
|
jsdoc@^3.6.11:
|
||||||
|
version "3.6.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.6.11.tgz#8bbb5747e6f579f141a5238cbad4e95e004458ce"
|
||||||
|
integrity sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.9.4"
|
||||||
|
"@types/markdown-it" "^12.2.3"
|
||||||
|
bluebird "^3.7.2"
|
||||||
|
catharsis "^0.9.0"
|
||||||
|
escape-string-regexp "^2.0.0"
|
||||||
|
js2xmlparser "^4.0.2"
|
||||||
|
klaw "^3.0.0"
|
||||||
|
markdown-it "^12.3.2"
|
||||||
|
markdown-it-anchor "^8.4.1"
|
||||||
|
marked "^4.0.10"
|
||||||
|
mkdirp "^1.0.4"
|
||||||
|
requizzle "^0.2.3"
|
||||||
|
strip-json-comments "^3.1.0"
|
||||||
|
taffydb "2.6.2"
|
||||||
|
underscore "~1.13.2"
|
||||||
|
|
||||||
jsesc@^2.5.1:
|
jsesc@^2.5.1:
|
||||||
version "2.5.2"
|
version "2.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
|
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
|
||||||
|
@ -11427,6 +11515,13 @@ kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||||
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
||||||
|
|
||||||
|
klaw@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/klaw/-/klaw-3.0.0.tgz#b11bec9cf2492f06756d6e809ab73a2910259146"
|
||||||
|
integrity sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.1.9"
|
||||||
|
|
||||||
kleur@^3.0.3:
|
kleur@^3.0.3:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||||
|
@ -11587,6 +11682,13 @@ lines-and-columns@^2.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b"
|
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b"
|
||||||
integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==
|
integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==
|
||||||
|
|
||||||
|
linkify-it@^3.0.1:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e"
|
||||||
|
integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==
|
||||||
|
dependencies:
|
||||||
|
uc.micro "^1.0.1"
|
||||||
|
|
||||||
lint-staged@^13.0.3:
|
lint-staged@^13.0.3:
|
||||||
version "13.0.3"
|
version "13.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.0.3.tgz#d7cdf03a3830b327a2b63c6aec953d71d9dc48c6"
|
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.0.3.tgz#d7cdf03a3830b327a2b63c6aec953d71d9dc48c6"
|
||||||
|
@ -12182,11 +12284,32 @@ markdown-extensions@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-1.1.1.tgz#fea03b539faeaee9b4ef02a3769b455b189f7fc3"
|
resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-1.1.1.tgz#fea03b539faeaee9b4ef02a3769b455b189f7fc3"
|
||||||
integrity sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==
|
integrity sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==
|
||||||
|
|
||||||
|
markdown-it-anchor@^8.4.1:
|
||||||
|
version "8.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz#30c4bc5bbff327f15ce3c429010ec7ba75e7b5f8"
|
||||||
|
integrity sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==
|
||||||
|
|
||||||
|
markdown-it@^12.3.2:
|
||||||
|
version "12.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90"
|
||||||
|
integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==
|
||||||
|
dependencies:
|
||||||
|
argparse "^2.0.1"
|
||||||
|
entities "~2.1.0"
|
||||||
|
linkify-it "^3.0.1"
|
||||||
|
mdurl "^1.0.1"
|
||||||
|
uc.micro "^1.0.5"
|
||||||
|
|
||||||
markdown-table@^3.0.0:
|
markdown-table@^3.0.0:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.2.tgz#9b59eb2c1b22fe71954a65ff512887065a7bb57c"
|
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.2.tgz#9b59eb2c1b22fe71954a65ff512887065a7bb57c"
|
||||||
integrity sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==
|
integrity sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==
|
||||||
|
|
||||||
|
marked@^4.0.10:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/marked/-/marked-4.1.0.tgz#3fc6e7485f21c1ca5d6ec4a39de820e146954796"
|
||||||
|
integrity sha512-+Z6KDjSPa6/723PQYyc1axYZpYYpDnECDaU6hkaf5gqBieBkMKYReL5hteF2QizhlMbgbo8umXl/clZ67+GlsA==
|
||||||
|
|
||||||
matcher@^5.0.0:
|
matcher@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/matcher/-/matcher-5.0.0.tgz#cd82f1c7ae7ee472a9eeaf8ec7cac45e0fe0da62"
|
resolved "https://registry.yarnpkg.com/matcher/-/matcher-5.0.0.tgz#cd82f1c7ae7ee472a9eeaf8ec7cac45e0fe0da62"
|
||||||
|
@ -12537,7 +12660,7 @@ mdast-util-toc@^6.1.0:
|
||||||
unist-util-is "^5.0.0"
|
unist-util-is "^5.0.0"
|
||||||
unist-util-visit "^3.0.0"
|
unist-util-visit "^3.0.0"
|
||||||
|
|
||||||
mdurl@^1.0.0:
|
mdurl@^1.0.0, mdurl@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
||||||
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
|
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
|
||||||
|
@ -15353,6 +15476,14 @@ posix-character-classes@^0.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||||
integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==
|
integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==
|
||||||
|
|
||||||
|
postcss-for@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-for/-/postcss-for-2.1.1.tgz#841378c0ef909d50e1980d5aa71e6a340e728fcd"
|
||||||
|
integrity sha512-X0R84FCyr5cqzW4+/g4Dvz2OUe1iwC3G/atIrwEpiRstZlBBpknV+ETlIneSTnw/iXgUnEoTRaO2qXY62YWLhQ==
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.0"
|
||||||
|
postcss-simple-vars "^2.0.0"
|
||||||
|
|
||||||
postcss-import@^14.1.0:
|
postcss-import@^14.1.0:
|
||||||
version "14.1.0"
|
version "14.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0"
|
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0"
|
||||||
|
@ -15392,6 +15523,13 @@ postcss-selector-parser@6.0.10, postcss-selector-parser@^6.0.10, postcss-selecto
|
||||||
cssesc "^3.0.0"
|
cssesc "^3.0.0"
|
||||||
util-deprecate "^1.0.2"
|
util-deprecate "^1.0.2"
|
||||||
|
|
||||||
|
postcss-simple-vars@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-simple-vars/-/postcss-simple-vars-2.0.0.tgz#d0a1091b0da22b79507028f7b22b976c0a60b8d5"
|
||||||
|
integrity sha512-HllLaKKCBOdKudyzqrw/ve5rWouM9cDL+WHaSF9q4CkBEPjdTdiKNw1xF2dAz5rUKrxVmnUmOYxamwy37dnq2Q==
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.21"
|
||||||
|
|
||||||
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
|
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||||
|
@ -15415,6 +15553,16 @@ postcss@8.4.14:
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
|
postcss@^5.0.0, postcss@^5.0.21:
|
||||||
|
version "5.2.18"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
|
||||||
|
integrity sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==
|
||||||
|
dependencies:
|
||||||
|
chalk "^1.1.3"
|
||||||
|
js-base64 "^2.1.9"
|
||||||
|
source-map "^0.5.6"
|
||||||
|
supports-color "^3.2.3"
|
||||||
|
|
||||||
postcss@^8.4.12, postcss@^8.4.14, postcss@^8.4.4, postcss@^8.4.5:
|
postcss@^8.4.12, postcss@^8.4.14, postcss@^8.4.4, postcss@^8.4.5:
|
||||||
version "8.4.16"
|
version "8.4.16"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
|
||||||
|
@ -16933,6 +17081,13 @@ requires-port@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||||
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
|
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
|
||||||
|
|
||||||
|
requizzle@^0.2.3:
|
||||||
|
version "0.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.3.tgz#4675c90aacafb2c036bd39ba2daa4a1cb777fded"
|
||||||
|
integrity sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==
|
||||||
|
dependencies:
|
||||||
|
lodash "^4.17.14"
|
||||||
|
|
||||||
resolve-cwd@^3.0.0:
|
resolve-cwd@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||||
|
@ -18195,6 +18350,13 @@ supports-color@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||||
integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==
|
integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==
|
||||||
|
|
||||||
|
supports-color@^3.2.3:
|
||||||
|
version "3.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
|
||||||
|
integrity sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==
|
||||||
|
dependencies:
|
||||||
|
has-flag "^1.0.0"
|
||||||
|
|
||||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
|
@ -18251,6 +18413,11 @@ tabtab@^3.0.2:
|
||||||
mkdirp "^0.5.1"
|
mkdirp "^0.5.1"
|
||||||
untildify "^3.0.3"
|
untildify "^3.0.3"
|
||||||
|
|
||||||
|
taffydb@2.6.2:
|
||||||
|
version "2.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268"
|
||||||
|
integrity sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==
|
||||||
|
|
||||||
tailwindcss-open-variant@^1.0.0:
|
tailwindcss-open-variant@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/tailwindcss-open-variant/-/tailwindcss-open-variant-1.0.0.tgz#e4555c0a0ec2a82801e563ed36b1b23e4dd04a3b"
|
resolved "https://registry.yarnpkg.com/tailwindcss-open-variant/-/tailwindcss-open-variant-1.0.0.tgz#e4555c0a0ec2a82801e563ed36b1b23e4dd04a3b"
|
||||||
|
@ -18846,6 +19013,11 @@ typical@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"
|
resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"
|
||||||
integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==
|
integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==
|
||||||
|
|
||||||
|
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||||
|
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
|
||||||
|
|
||||||
uglify-js@^3.1.4:
|
uglify-js@^3.1.4:
|
||||||
version "3.16.2"
|
version "3.16.2"
|
||||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.2.tgz#0481e1dbeed343ad1c2ddf3c6d42e89b7a6d4def"
|
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.2.tgz#0481e1dbeed343ad1c2ddf3c6d42e89b7a6d4def"
|
||||||
|
@ -18881,7 +19053,7 @@ undefsafe@^2.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
|
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
|
||||||
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
|
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
|
||||||
|
|
||||||
underscore@^1.5.0:
|
underscore@^1.5.0, underscore@~1.13.2:
|
||||||
version "1.13.4"
|
version "1.13.4"
|
||||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee"
|
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee"
|
||||||
integrity sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==
|
integrity sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==
|
||||||
|
@ -19950,6 +20122,11 @@ xml-js@^1.6.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
sax "^1.2.4"
|
sax "^1.2.4"
|
||||||
|
|
||||||
|
xmlcreate@^2.0.4:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.4.tgz#0c5ab0f99cdd02a81065fa9cd8f8ae87624889be"
|
||||||
|
integrity sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==
|
||||||
|
|
||||||
xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
|
xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue