wip(core): Work on v3
This commit is contained in:
parent
cd76e75101
commit
e4b5d52aaf
3 changed files with 77 additions and 135 deletions
|
@ -1,43 +1,40 @@
|
||||||
import Pattern from './pattern.mjs'
|
import Pattern from './pattern.mjs'
|
||||||
import { addPartConfig } from './utils.mjs'
|
import { addPartConfig } from './utils.mjs'
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Design constructor. Returns a Pattern constructor
|
* The Design constructor. Returns a Pattern constructor
|
||||||
* So it's sort of a super-constructor
|
* So it's sort of a super-constructor
|
||||||
*/
|
*/
|
||||||
export default function Design(config, plugins = false, conditionalPlugins = false) {
|
export default function Design(config) {
|
||||||
// Add part options/measurements/optionalMeasurements to config
|
|
||||||
if (!config.options) config.options = {}
|
|
||||||
if (!config.measurements) config.measurements = []
|
|
||||||
if (!config.optionalMeasurements) config.optionalMeasurements = []
|
|
||||||
if (Array.isArray(config.parts)) {
|
|
||||||
const parts = {}
|
|
||||||
for (const part of config.parts) {
|
|
||||||
if (typeof part === 'object') {
|
|
||||||
parts[part.name] = part
|
|
||||||
config = addPartConfig(parts[part.name], config)
|
|
||||||
} else if (typeof part === 'string') {
|
|
||||||
parts[part] = part
|
|
||||||
} else throw("Part should be passed as a name of part config object")
|
|
||||||
}
|
|
||||||
config.parts = parts
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure all options have a hide() method
|
// Merge config with defaults
|
||||||
config.options = optionsWithHide(config.options)
|
config = {
|
||||||
|
parts: [],
|
||||||
|
options: {},
|
||||||
|
measurements: [],
|
||||||
|
optionalMeasurements: [],
|
||||||
|
plugins: [],
|
||||||
|
conditionalPlugins: [],
|
||||||
|
...config
|
||||||
|
}
|
||||||
|
const parts = {}
|
||||||
|
for (const part of config.parts) {
|
||||||
|
if (typeof part === 'object') {
|
||||||
|
parts[part.name] = part
|
||||||
|
config = addPartConfig(parts[part.name], config)
|
||||||
|
}
|
||||||
|
else throw("Invalid part configuration. Part is not an object")
|
||||||
|
}
|
||||||
|
// Replace config.parts with the resolved config
|
||||||
|
config.parts = parts
|
||||||
|
|
||||||
|
// Ensure all options have a hide() method and menu property
|
||||||
|
config.options = completeOptions(config.options)
|
||||||
|
|
||||||
// A place to store deprecation and other warnings before we even have a pattern instantiated
|
// A place to store deprecation and other warnings before we even have a pattern instantiated
|
||||||
config.warnings = []
|
config.warnings = []
|
||||||
|
|
||||||
/*
|
|
||||||
* The newer way to initalize a design is to pass one single parameter
|
|
||||||
* The old way passed multiple parameters.
|
|
||||||
* So let's figure out which is which and be backwards compatible
|
|
||||||
*
|
|
||||||
* This mitigation should be removed in v3 when we drop support for the legacy way
|
|
||||||
*/
|
|
||||||
config = migrateConfig(config, plugins, conditionalPlugins)
|
|
||||||
|
|
||||||
const pattern = function (settings) {
|
const pattern = function (settings) {
|
||||||
Pattern.call(this, config)
|
Pattern.call(this, config)
|
||||||
|
|
||||||
|
@ -66,55 +63,22 @@ export default function Design(config, plugins = false, conditionalPlugins = fal
|
||||||
return pattern
|
return pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper method to handle the legacy way of passing configuration
|
|
||||||
* to the design constructor
|
|
||||||
*/
|
|
||||||
const migrateConfig = (config, plugins, conditionalPlugins) => {
|
|
||||||
|
|
||||||
// Migrate plugins
|
|
||||||
if (plugins && config.plugins) config.warnings.push(
|
|
||||||
'Passing plugins to the Design constructor both as a second parameter and in the config is unsupported',
|
|
||||||
'Ignoring plugins passed as parameter. Only config.plugins will be used.'
|
|
||||||
)
|
|
||||||
else if (plugins && !config.plugins) {
|
|
||||||
config.plugins = plugins
|
|
||||||
config.warnings.push(
|
|
||||||
'Passing a plugins parameter to the Design constructure is deprecated',
|
|
||||||
'Please store them in the `plugins` key of the config object that is the first parameter'
|
|
||||||
)
|
|
||||||
} else if (!config.plugins) config.plugins = []
|
|
||||||
|
|
||||||
// Migrate conditional plugins
|
|
||||||
if (conditionalPlugins && config.conditionalPlugins) config.warnings.push(
|
|
||||||
'Passing conditionalPlugins to the Design constructor both as a third parameter and in the config is unsupported.',
|
|
||||||
'Ignoring conditionalPlugins passes as parameter. Only config.conditionalPlugins will be used.',
|
|
||||||
)
|
|
||||||
else if (conditionalPlugins && !config.conditionalPlugins) {
|
|
||||||
config.conditionalPlugins = conditionalPlugins
|
|
||||||
config.warnings.push(
|
|
||||||
'Passing a conditionalPlugins parameter to the Design constructure is deprecated.',
|
|
||||||
'Please store them in the `conditionalPlugins` key of the config object that is the first parameter'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else if (!config.conditionalPlugins) config.conditionalPlugins = []
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A default hide() method for options that lack it
|
* A default hide() method for options that lack it
|
||||||
* Since this will always return false, the option will never be hidden
|
* As this always return false, the option will never be hidden
|
||||||
*/
|
*/
|
||||||
const hide = () => false // The default hide() method
|
const hide = () => false
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper method to add the default hide() method to options who lack one
|
* Helper method to add the default hide() method to options who lack one
|
||||||
|
* as well as set the `menu` property to false (if it's missing)
|
||||||
*/
|
*/
|
||||||
const optionsWithHide = options => {
|
const completeOptions = options => {
|
||||||
if (options) {
|
if (options) {
|
||||||
for (const option in options) {
|
for (const option in options) {
|
||||||
if (typeof options[option] === 'object') options[option] = { hide, ...options[option] }
|
if (typeof options[option] === 'object') {
|
||||||
|
options[option] = { hide, menu: false, ...options[option] }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,8 @@ Part.prototype.getId = function (prefix = '') {
|
||||||
|
|
||||||
/** Returns a value formatted for units provided in settings */
|
/** Returns a value formatted for units provided in settings */
|
||||||
Part.prototype.unitsClosure = function (value) {
|
Part.prototype.unitsClosure = function (value) {
|
||||||
let self = this
|
const self = this
|
||||||
let method = function (value) {
|
const method = function (value) {
|
||||||
if (self.context.settings.debug && typeof value !== 'number')
|
if (self.context.settings.debug && typeof value !== 'number')
|
||||||
self.context.raise.debug(
|
self.context.raise.debug(
|
||||||
`Calling \`units(value)\` but \`value\` is not a number (\`${typeof value}\`)`
|
`Calling \`units(value)\` but \`value\` is not a number (\`${typeof value}\`)`
|
||||||
|
|
|
@ -15,7 +15,7 @@ import Svg from './svg.mjs'
|
||||||
import Store from './store.mjs'
|
import Store from './store.mjs'
|
||||||
import Hooks from './hooks.mjs'
|
import Hooks from './hooks.mjs'
|
||||||
import Attributes from './attributes.mjs'
|
import Attributes from './attributes.mjs'
|
||||||
import pkg from '../package.json'
|
import { version } from '../package.json' assert { type: 'json' }
|
||||||
|
|
||||||
export default function Pattern(config = { options: {} }) {
|
export default function Pattern(config = { options: {} }) {
|
||||||
|
|
||||||
|
@ -28,41 +28,45 @@ export default function Pattern(config = { options: {} }) {
|
||||||
margin: 2,
|
margin: 2,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
layout: true,
|
layout: true,
|
||||||
debug: true,
|
debug: false,
|
||||||
options: {},
|
options: {},
|
||||||
absoluteOptions: {},
|
absoluteOptions: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object to hold events
|
// Object to hold events
|
||||||
this.events = {
|
this.events = {
|
||||||
info: [],
|
|
||||||
warning: [],
|
|
||||||
error: [],
|
|
||||||
debug: [],
|
debug: [],
|
||||||
|
error: [],
|
||||||
|
info: [],
|
||||||
|
suggestion: [],
|
||||||
|
warning: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raise methods - Make events and settings avialable in them
|
// Raise methods - Make events and settings avialable in them
|
||||||
const events = this.events
|
const events = this.events
|
||||||
const settings = this.settings
|
const settings = this.settings
|
||||||
this.raise = {
|
this.raise = {
|
||||||
|
debug: function (data) {
|
||||||
|
// Debug only if debug is active
|
||||||
|
if (settings.debug) events.debug.push(data)
|
||||||
|
},
|
||||||
|
error: function (data) {
|
||||||
|
events.error.push(data)
|
||||||
|
},
|
||||||
info: function (data) {
|
info: function (data) {
|
||||||
events.info.push(data)
|
events.info.push(data)
|
||||||
},
|
},
|
||||||
|
suggestion: function (data) {
|
||||||
|
events.info.push(data)
|
||||||
|
},
|
||||||
warning: function (data) {
|
warning: function (data) {
|
||||||
events.warning.push(data)
|
events.warning.push(data)
|
||||||
},
|
},
|
||||||
error: function (data) {
|
|
||||||
events.error.push(data)
|
|
||||||
},
|
|
||||||
debug: function (data) {
|
|
||||||
// Debug only if debug is active
|
|
||||||
if (settings.debug) events.debug.push(data)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Say hi
|
// Say hi
|
||||||
this.raise.info(
|
this.raise.info(
|
||||||
`New \`@freesewing/${config.name}:${config.version}\` pattern using \`@freesewing/core:${pkg.version}\``
|
`New \`@freesewing/${config.name}:${config.version}\` pattern using \`@freesewing/core:${version}\``
|
||||||
)
|
)
|
||||||
|
|
||||||
// More things that go in a pattern
|
// More things that go in a pattern
|
||||||
|
@ -72,7 +76,6 @@ export default function Pattern(config = { options: {} }) {
|
||||||
this.is = '' // Will be set when drafting/sampling
|
this.is = '' // Will be set when drafting/sampling
|
||||||
this.autoLayout = { parts: {} } // Will hold auto-generated layout
|
this.autoLayout = { parts: {} } // Will hold auto-generated layout
|
||||||
this.cutList = {} // Will hold the cutlist
|
this.cutList = {} // Will hold the cutlist
|
||||||
|
|
||||||
this.store = new Store(this.raise) // Store for sharing data across parts
|
this.store = new Store(this.raise) // Store for sharing data across parts
|
||||||
this.parts = {} // Parts container
|
this.parts = {} // Parts container
|
||||||
this.hooks = new Hooks() // Hooks container
|
this.hooks = new Hooks() // Hooks container
|
||||||
|
@ -81,6 +84,7 @@ export default function Pattern(config = { options: {} }) {
|
||||||
this.Snippet = Snippet // Snippet constructor
|
this.Snippet = Snippet // Snippet constructor
|
||||||
this.Attributes = Attributes // Attributes constructor
|
this.Attributes = Attributes // Attributes constructor
|
||||||
this.initialized = 0 // Keep track of init calls
|
this.initialized = 0 // Keep track of init calls
|
||||||
|
this.macros = {} // Macros
|
||||||
|
|
||||||
if (typeof this.config.dependencies === 'undefined') this.config.dependencies = {}
|
if (typeof this.config.dependencies === 'undefined') this.config.dependencies = {}
|
||||||
if (typeof this.config.inject === 'undefined') this.config.inject = {}
|
if (typeof this.config.inject === 'undefined') this.config.inject = {}
|
||||||
|
@ -94,9 +98,6 @@ export default function Pattern(config = { options: {} }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Macros
|
|
||||||
this.macros = {}
|
|
||||||
|
|
||||||
// Context object to add to Part closure
|
// Context object to add to Part closure
|
||||||
const context = {
|
const context = {
|
||||||
parts: this.parts,
|
parts: this.parts,
|
||||||
|
@ -148,11 +149,18 @@ Pattern.prototype.addOptions = function(options={}) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Utility method to get the (initialized) config */
|
||||||
Pattern.prototype.getConfig = function () {
|
Pattern.prototype.getConfig = function () {
|
||||||
this.init()
|
this.init()
|
||||||
return this.config
|
return this.config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Utility method to get the (initialized) part list */
|
||||||
|
Pattern.prototype.getPartList = function () {
|
||||||
|
this.init()
|
||||||
|
return Object.keys(this.config.parts) || []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defer some things that used to happen in the constructor to
|
* Defer some things that used to happen in the constructor to
|
||||||
|
@ -163,11 +171,8 @@ Pattern.prototype.init = function () {
|
||||||
// Resolve all dependencies
|
// Resolve all dependencies
|
||||||
this.dependencies = this.config.dependencies
|
this.dependencies = this.config.dependencies
|
||||||
this.inject = this.config.inject
|
this.inject = this.config.inject
|
||||||
this.hide = this.config.hide
|
this.__parts = this.config.parts
|
||||||
if (typeof this.config.parts === 'object') {
|
this.preresolveDependencies()
|
||||||
this.__parts = this.config.parts
|
|
||||||
this.preresolveDependencies()
|
|
||||||
}
|
|
||||||
this.resolvedDependencies = this.resolveDependencies(this.dependencies)
|
this.resolvedDependencies = this.resolveDependencies(this.dependencies)
|
||||||
this.config.resolvedDependencies = this.resolvedDependencies
|
this.config.resolvedDependencies = this.resolvedDependencies
|
||||||
this.config.draftOrder = this.draftOrder(this.resolvedDependencies)
|
this.config.draftOrder = this.draftOrder(this.resolvedDependencies)
|
||||||
|
@ -250,8 +255,7 @@ Pattern.prototype.runHooks = function (hookName, data = false) {
|
||||||
/*
|
/*
|
||||||
* Allows adding a part at run-time
|
* Allows adding a part at run-time
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.addPart = function (part, name=false) {
|
Pattern.prototype.addPart = function (part) {
|
||||||
if (!part.draft) part = decoratePartDependency(part, name)
|
|
||||||
if (typeof part?.draft === 'function') {
|
if (typeof part?.draft === 'function') {
|
||||||
if (part.name) {
|
if (part.name) {
|
||||||
this.config.parts[part.name] = part
|
this.config.parts[part.name] = part
|
||||||
|
@ -260,7 +264,7 @@ Pattern.prototype.addPart = function (part, name=false) {
|
||||||
}
|
}
|
||||||
else this.raise.error(`Part must have a name`)
|
else this.raise.error(`Part must have a name`)
|
||||||
}
|
}
|
||||||
else this.raise.warning(`Cannot attach part ${name} because it is not a part`)
|
else this.raise.error(`Part must have a draft() method`)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -309,7 +313,6 @@ Pattern.prototype.draft = function () {
|
||||||
}
|
}
|
||||||
if (this.needs(partName)) {
|
if (this.needs(partName)) {
|
||||||
// Draft part
|
// Draft part
|
||||||
const method = 'draft' + capitalize(partName)
|
|
||||||
if (typeof this.__parts?.[partName]?.draft === 'function') {
|
if (typeof this.__parts?.[partName]?.draft === 'function') {
|
||||||
// 2022 way - Part is contained in config
|
// 2022 way - Part is contained in config
|
||||||
try {
|
try {
|
||||||
|
@ -319,25 +322,7 @@ Pattern.prototype.draft = function () {
|
||||||
this.raise.error([`Unable to draft part \`${partName}\``, err])
|
this.raise.error([`Unable to draft part \`${partName}\``, err])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (typeof this[method] === 'function') {
|
else this.raise.error(`Unable to draft pattern. Part.draft() is not callable`)
|
||||||
// Legacy way - Part is attached to the prototype
|
|
||||||
this.raise.warning(`Attaching part methods to the Pattern prototype is deprecated and will be removed in FreeSewing v3 (part: \`${partName}\`)`)
|
|
||||||
try {
|
|
||||||
this.parts[partName] = this[method](this.parts[partName])
|
|
||||||
if (this.parts[partName].render ) this.cutList[partName] = this.parts[partName].cut
|
|
||||||
} catch (err) {
|
|
||||||
this.raise.error([`Unable to draft part \`${partName}\``, err])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.raise.error(`Unable to draft pattern. Part is not available in iether legacy or 2022`)
|
|
||||||
throw new Error('Method "' + method + '" on pattern object is not callable')
|
|
||||||
}
|
|
||||||
if (typeof this.parts[partName] === 'undefined') {
|
|
||||||
this.raise.error(
|
|
||||||
`Result of \`pattern.${method}\` was \`undefined\`. Did you forget to return the \`Part\` object?`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
this.parts[partName].render =
|
this.parts[partName].render =
|
||||||
this.parts[partName].render === false ? false : this.wants(partName)
|
this.parts[partName].render === false ? false : this.wants(partName)
|
||||||
|
@ -689,7 +674,7 @@ Pattern.prototype.addDependency = function (name, part, dep) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Filter optional measurements out of they are also required measurements */
|
/** Filter optional measurements out if they are also required measurements */
|
||||||
Pattern.prototype.filterOptionalMeasurements = function () {
|
Pattern.prototype.filterOptionalMeasurements = function () {
|
||||||
this.config.optionalMeasurements = this.config.optionalMeasurements.filter(
|
this.config.optionalMeasurements = this.config.optionalMeasurements.filter(
|
||||||
m => this.config.measurements.indexOf(m) === -1
|
m => this.config.measurements.indexOf(m) === -1
|
||||||
|
@ -755,11 +740,11 @@ Pattern.prototype.resolveDependencies = function (graph = this.dependencies) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include parts outside the dependency graph
|
// Include parts outside the dependency graph
|
||||||
if (typeof this.config.parts === 'object') {
|
//if (typeof this.config.parts === 'object') {
|
||||||
for (const part of Object.values(this.config.parts)) {
|
// for (const part of Object.values(this.config.parts)) {
|
||||||
if (typeof part === 'string' && typeof this.dependencies[part] === 'undefined') this.dependencies[part] = []
|
// if (typeof part === 'string' && typeof this.dependencies[part] === 'undefined') this.dependencies[part] = []
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
let resolved = {}
|
let resolved = {}
|
||||||
let seen = {}
|
let seen = {}
|
||||||
|
@ -796,26 +781,19 @@ Pattern.prototype.needs = function (partName) {
|
||||||
|
|
||||||
/* Checks whether a part is hidden in the config */
|
/* Checks whether a part is hidden in the config */
|
||||||
Pattern.prototype.isHidden = function (partName) {
|
Pattern.prototype.isHidden = function (partName) {
|
||||||
if (Array.isArray(this.hide)) {
|
return (this.__parts?.[partName]?.hide) ? true : false
|
||||||
if (this.hide.indexOf(partName) !== -1) return true
|
|
||||||
}
|
|
||||||
// 2022 style
|
|
||||||
if (this.__parts?.[partName]?.hide) return true
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Determines whether a part is wanted by the user
|
/** Determines whether a part is wanted by the user
|
||||||
* This depends on the 'only' setting
|
* This depends on the 'only' setting
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.wants = function (partName) {
|
Pattern.prototype.wants = function (partName) {
|
||||||
if (typeof this.settings.only === 'undefined' || this.settings.only === false) {
|
if (!this.settings?.only === 'undefined' || this.settings.only === false) {
|
||||||
if (this.isHidden(partName)) return false
|
if (this.isHidden(partName)) return false
|
||||||
} else if (typeof this.settings.only === 'string') {
|
}
|
||||||
if (this.settings.only === partName) return true
|
else if (typeof this.settings.only === 'string') return (this.settings.only === partName)
|
||||||
return false
|
else if (Array.isArray(this.settings.only)) {
|
||||||
} else if (Array.isArray(this.settings.only)) {
|
for (const part of this.settings.only) {
|
||||||
for (let part of this.settings.only) {
|
|
||||||
if (part === partName) return true
|
if (part === partName) return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -851,8 +829,8 @@ Pattern.prototype.getRenderProps = function () {
|
||||||
props.events = {
|
props.events = {
|
||||||
debug: this.events.debug,
|
debug: this.events.debug,
|
||||||
info: this.events.info,
|
info: this.events.info,
|
||||||
warning: this.events.warning,
|
|
||||||
error: this.events.error,
|
error: this.events.error,
|
||||||
|
warning: this.events.warning,
|
||||||
}
|
}
|
||||||
props.cutList = this.cutList
|
props.cutList = this.cutList
|
||||||
props.parts = {}
|
props.parts = {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue