diff --git a/packages/core/src/attributes.mjs b/packages/core/src/attributes.mjs index f04a1211b67..211c7b28fe6 100644 --- a/packages/core/src/attributes.mjs +++ b/packages/core/src/attributes.mjs @@ -35,7 +35,7 @@ Attributes.prototype.add = function (name, value) { } /** - * Return a props object for attributes with a fiven prefix (typically used for data-text) + * Return a props object for attributes with a given prefix (typically used for data-text) * * @param {string} prefix - The prefix to filter attributes on * @return {object} props - The attributes as props diff --git a/packages/core/src/defs.mjs b/packages/core/src/defs.mjs new file mode 100644 index 00000000000..e12ef530029 --- /dev/null +++ b/packages/core/src/defs.mjs @@ -0,0 +1,93 @@ +////////////////////////////////////////////// +// CONSTRUCTOR // +////////////////////////////////////////////// + +/** + * Constructor for Defs + * + * @constructor + * @return {Defs} this - The Defs instance + */ +export function Defs() { + this.list = {} + + return this +} + +////////////////////////////////////////////// +// PUBLIC METHODS // +////////////////////////////////////////////// + +/** + * Return a deep copy of this + * + * @return {object} this - The Defs instance + */ +Defs.prototype.clone = function () { + let clone = new Defs() + clone.list = JSON.parse(JSON.stringify(this.list)) + + return clone +} + +/** + * Retrieve a def + * + * @param {string} name - Name of the def to get + * @return value - The value under name + */ +Defs.prototype.get = function (name) { + if (typeof this.list[name] === 'undefined') return false + else return this.list[name] +} + +/** + * Remove a def + * + * @param {string} name - Name of the def to set + * @return {object} this - The Defs instance + */ +Defs.prototype.remove = function (name) { + delete this.list[name] + + return this +} + +/** + * Return SVG code for Defs + * + * @return {string} svg - The SVG code + */ +Defs.prototype.render = function () { + let svg = '' + for (let key in this.list) { + svg += ` ${key}="${this.list[key]}"` + } + + return svg +} + +/** + * Set a def, overwriting existing value + * + * @param {string} name - Name of the def to set + * @param {string} value - Value of the def to set + * @return {Defs} this - The Defs instance + */ +Defs.prototype.set = function (name, value) { + this.list[name] = value + + return this +} + +/** + * Sets a def, but only if it's not currently set + * + * @param {string} name - Name of the def to set + * @param {string} value - Value of the def to set + * @return {Defs} this - The Defs instance + */ +Defs.prototype.setIfUnset = function (name, value) { + if (typeof this.list[name] === 'undefined') this.list[name] = value + return this +} diff --git a/packages/core/src/svg.mjs b/packages/core/src/svg.mjs index d77412ce6ac..0dd58f963ea 100644 --- a/packages/core/src/svg.mjs +++ b/packages/core/src/svg.mjs @@ -1,4 +1,5 @@ import { Attributes } from './attributes.mjs' +import { Defs } from './defs.mjs' import { __addNonEnumProp, round } from './utils.mjs' import { version } from '../data.mjs' @@ -31,7 +32,7 @@ export function Svg(pattern) { this.layout = {} this.body = '' this.style = '' - this.defs = '' + this.defs = new Defs() } ////////////////////////////////////////////// @@ -215,7 +216,7 @@ Svg.prototype.__renderCircle = function (point) { Svg.prototype.__renderDefs = function () { let svg = '' this.__indent() - svg += this.__nl() + this.defs + svg += this.__nl() + this.defs.render() this.__outdent() svg += this.__nl() + '' + this.__nl() diff --git a/packages/core/tests/defs.test.mjs b/packages/core/tests/defs.test.mjs new file mode 100644 index 00000000000..f603c600f80 --- /dev/null +++ b/packages/core/tests/defs.test.mjs @@ -0,0 +1,38 @@ +import chai from 'chai' +import { Defs } from '../src/defs.mjs' + +const expect = chai.expect + +describe('Defs', () => { + let defs = new Defs() + + it('Should set a def', () => { + defs.set('test', 'passed') + expect(defs.get('test')).to.equal('passed') + }) + + it('Should remove a def', () => { + defs.remove('test') + expect(defs.get('test')).to.equal(false) + }) + + it('Should only set an unset def', () => { + defs.setIfUnset('test', 'passed') + expect(defs.get('test')).to.equal('passed') + defs.setIfUnset('test', 'failed') + expect(defs.get('test')).to.equal('passed') + }) + + it('Should return false when getting an unset def', () => { + expect(defs.get('doNotTest')).to.equal(false) + }) + + it('Should render defs correctly', () => { + console.log(defs.render()) + expect(defs.render()).to.equal(' test="passed"') + }) + + it('Should be able to clone itself', () => { + expect(defs.clone().get('test')).to.equal('passed') + }) +}) diff --git a/packages/core/tests/svg.test.mjs b/packages/core/tests/svg.test.mjs index 0306e5767f5..664f6f86434 100644 --- a/packages/core/tests/svg.test.mjs +++ b/packages/core/tests/svg.test.mjs @@ -2,6 +2,7 @@ import chai from 'chai' import chaiString from 'chai-string' import { Svg } from '../src/svg.mjs' import { Design, Attributes } from '../src/index.mjs' +import { Defs } from '../src/defs.mjs' import { version } from '../data.mjs' import render from './fixtures/render.mjs' @@ -42,7 +43,7 @@ describe('Svg', () => { expect(svg.freeId).to.equal(0) expect(svg.body).to.equal('') expect(svg.style).to.equal('') - expect(svg.defs).to.equal('') + expect(svg.defs).to.be.an.instanceof(Defs) expect(svg.prefix).to.equal('') expect(svg.attributes.get('xmlns')).to.equal('http://www.w3.org/2000/svg') expect(svg.attributes.get('xmlns:svg')).to.equal('http://www.w3.org/2000/svg') diff --git a/plugins/plugin-annotations/src/buttons.mjs b/plugins/plugin-annotations/src/buttons.mjs index e49f78c36c4..cf870fbdd4d 100644 --- a/plugins/plugin-annotations/src/buttons.mjs +++ b/plugins/plugin-annotations/src/buttons.mjs @@ -1,6 +1,8 @@ -const defs = [ - // button - ` +// Export defs +export const buttonsDefs = [ + { + name: 'button', + def: ` `, - // buttonhole - ` + }, + { + name: 'buttonhole', + def: ` - +`, + }, + { + name: 'buttonhole-start', + def: ` - +`, + }, + { + name: 'buttonhole-end', + def: ` `, - // snaps - ` + }, + { + name: 'snap-stud-grad', + def: ` - +`, + }, + { + name: 'snap-stud', + def: ` - +`, + }, + { + name: 'snap-socket', + def: ` `, + }, ] - -// Export hooks -export const buttonsHooks = { - preRender: [ - function (svg) { - for (const def of defs) { - if (svg.defs.indexOf(def) === -1) svg.defs += def - } - }, - ], -} diff --git a/plugins/plugin-annotations/src/cutonfold.mjs b/plugins/plugin-annotations/src/cutonfold.mjs index 3cc29d6a1c1..17f2ca08c77 100644 --- a/plugins/plugin-annotations/src/cutonfold.mjs +++ b/plugins/plugin-annotations/src/cutonfold.mjs @@ -1,20 +1,21 @@ -const markers = ` +// Export defs +export const cutonfoldDefs = [ + { + name: 'cutonfoldFrom', + def: ` - +`, + }, + { + name: 'cutonfoldTo', + def: ` - -` +`, + }, +] -// Export hooks -export const cutonfoldHooks = { - preRender: [ - function (svg) { - if (svg.defs.indexOf(markers) === -1) svg.defs += markers - }, - ], -} // Export macros export const cutonfoldMacros = { cutonfold: function (so, { points, paths, Path, complete, store, scale }) { diff --git a/plugins/plugin-annotations/src/dimensions.mjs b/plugins/plugin-annotations/src/dimensions.mjs index 52e777fc371..a4facf35b46 100644 --- a/plugins/plugin-annotations/src/dimensions.mjs +++ b/plugins/plugin-annotations/src/dimensions.mjs @@ -1,11 +1,21 @@ -const markers = ` +// Export defs +export const dimensionsDefs = [ + { + name: 'dimensionFrom', + def: ` - +`, + }, + { + name: 'dimensionTo', + def: ` - -` +`, + }, +] + const prefix = '__paperless' function drawDimension(from, to, so, { Path, units }) { @@ -70,14 +80,7 @@ function lleader(so, type, props, id) { return point } -// Export hooks and macros -export const dimensionsHooks = { - preRender: [ - function (svg) { - if (svg.defs.indexOf(markers) === -1) svg.defs += markers - }, - ], -} +// Export macros export const dimensionsMacros = { // horizontal hd: function (so, props) { diff --git a/plugins/plugin-annotations/src/grainline.mjs b/plugins/plugin-annotations/src/grainline.mjs index 92c90800ecf..be4c57024ee 100644 --- a/plugins/plugin-annotations/src/grainline.mjs +++ b/plugins/plugin-annotations/src/grainline.mjs @@ -1,21 +1,24 @@ -const markers = ` +// Export defs +export const grainlineDefs = [ + { + name: 'grainlineFrom', + def: ` - +`, + }, + { + name: 'grainlineTo', + def: ` -` +`, + }, +] const dflts = { text: 'grainline' } -// Export hooks and macros -export const grainlineHooks = { - preRender: [ - function (svg) { - if (svg.defs.indexOf(markers) === -1) svg.defs += markers - }, - ], -} +// Export macros export const grainlineMacros = { grainline: function (so = {}, { points, paths, Path, complete, store }) { if (so === false) { diff --git a/plugins/plugin-annotations/src/index.mjs b/plugins/plugin-annotations/src/index.mjs index 11cf575af5f..96be11fde67 100644 --- a/plugins/plugin-annotations/src/index.mjs +++ b/plugins/plugin-annotations/src/index.mjs @@ -1,8 +1,8 @@ import { name, version } from '../data.mjs' -// Hooks only -import { buttonsHooks } from './buttons.mjs' -import { logoHooks } from './logo.mjs' -import { notchesHooks } from './notches.mjs' +// Defs only +import { buttonsDefs } from './buttons.mjs' +import { logoDefs } from './logo.mjs' +import { notchesDefs } from './notches.mjs' // Macros only import { bannerMacros } from './banner.mjs' import { bannerboxMacros } from './bannerbox.mjs' @@ -11,26 +11,36 @@ import { crossboxMacros } from './crossbox.mjs' import { cutlistStores, cutlistHooks } from './cutlist.mjs' import { scaleboxMacros } from './scalebox.mjs' import { titleMacros } from './title.mjs' -// Hooks and Macros -import { cutonfoldMacros, cutonfoldHooks } from './cutonfold.mjs' -import { dimensionsMacros, dimensionsHooks } from './dimensions.mjs' -import { grainlineMacros, grainlineHooks } from './grainline.mjs' -import { pleatMacros, pleatHooks } from './pleat.mjs' -import { sewtogetherMacros, sewtogetherHooks } from './sewtogether.mjs' +// Defs and Macros +import { cutonfoldMacros, cutonfoldDefs } from './cutonfold.mjs' +import { dimensionsMacros, dimensionsDefs } from './dimensions.mjs' +import { grainlineMacros, grainlineDefs } from './grainline.mjs' +import { pleatMacros, pleatDefs } from './pleat.mjs' +import { sewtogetherMacros, sewtogetherDefs } from './sewtogether.mjs' export const plugin = { name, version, hooks: { preRender: [ - ...buttonsHooks.preRender, - ...logoHooks.preRender, - ...notchesHooks.preRender, - ...cutonfoldHooks.preRender, - ...dimensionsHooks.preRender, - ...grainlineHooks.preRender, - ...pleatHooks.preRender, - ...sewtogetherHooks.preRender, + function (svg) { + const defs = [ + ...buttonsDefs, + ...cutonfoldDefs, + ...dimensionsDefs, + ...grainlineDefs, + ...logoDefs, + ...notchesDefs, + ...pleatDefs, + ...sewtogetherDefs, + ] + for (const def of defs) { + svg.defs.setIfUnset( + def.name, + typeof def.def === 'function' ? def.def(svg.pattern.settings[0].scale) : def.def + ) + } + }, ], prePartDraft: [...cutlistHooks.prePartDraft], }, diff --git a/plugins/plugin-annotations/src/logo.mjs b/plugins/plugin-annotations/src/logo.mjs index 92639ea4fe2..40013a0dd41 100644 --- a/plugins/plugin-annotations/src/logo.mjs +++ b/plugins/plugin-annotations/src/logo.mjs @@ -1,11 +1,8 @@ -const logo = (scale) => - `` - -// Export hooks -export const logoHooks = { - preRender: [ - function (svg) { - if (svg.defs.indexOf('id="logo"') === -1) svg.defs += logo(svg.pattern.settings[0].scale) - }, - ], -} +// Export defs +export const logoDefs = [ + { + name: 'logo', + def: (scale) => + ``, + }, +] diff --git a/plugins/plugin-annotations/src/notches.mjs b/plugins/plugin-annotations/src/notches.mjs index 77764b38aa8..eccb5ed6fb7 100644 --- a/plugins/plugin-annotations/src/notches.mjs +++ b/plugins/plugin-annotations/src/notches.mjs @@ -1,18 +1,19 @@ -const markers = ` +// Export hooks +export const notchesDefs = [ + { + name: 'notch', + def: ` - +`, + }, + { + name: 'bnotch', + def: ` -` - -// Export hooks -export const notchesHooks = { - preRender: [ - function (svg) { - if (svg.defs.indexOf(`id="notch"`) === -1) svg.defs += markers - }, - ], -} +`, + }, +] diff --git a/plugins/plugin-annotations/src/pleat.mjs b/plugins/plugin-annotations/src/pleat.mjs index 0e37b1d85c0..3bc0f59c9ac 100644 --- a/plugins/plugin-annotations/src/pleat.mjs +++ b/plugins/plugin-annotations/src/pleat.mjs @@ -1,17 +1,14 @@ -const markers = ` +// Export defs +export const pleatDefs = [ + { + name: 'notch', + def: ` -` - -// Export hooks -export const pleatHooks = { - preRender: [ - function (svg) { - if (svg.defs.indexOf(markers) === -1) svg.defs += markers - }, - ], -} +`, + }, +] // Export macros export const pleatMacros = { diff --git a/plugins/plugin-annotations/src/sewtogether.mjs b/plugins/plugin-annotations/src/sewtogether.mjs index 16d2a4aa3f7..6884865e6a7 100644 --- a/plugins/plugin-annotations/src/sewtogether.mjs +++ b/plugins/plugin-annotations/src/sewtogether.mjs @@ -1,23 +1,27 @@ -const markers = ` +// Export defs +export const sewtogetherDefs = [ + { + name: 'sewTogetherStart', + def: ` - +`, + }, + { + name: 'sewTogetherEnd', + def: ` - +`, + }, + { + name: 'sewTogetherCross', + def: ` - -` - -// Export hooks -export const sewtogetherHooks = { - preRender: [ - function (svg) { - if (svg.defs.indexOf(markers) === -1) svg.defs += markers - }, - ], -} +`, + }, +] // Export macros export const sewtogetherMacros = { diff --git a/plugins/plugin-annotations/tests/buttons.test.mjs b/plugins/plugin-annotations/tests/buttons.test.mjs index ea2b28a4db3..3539cc90058 100644 --- a/plugins/plugin-annotations/tests/buttons.test.mjs +++ b/plugins/plugin-annotations/tests/buttons.test.mjs @@ -11,7 +11,7 @@ pattern.draft().render() describe('Buttons Plugin Test', () => { for (const snippet of ['button', 'buttonhole', 'snap-stud', 'snap-socket']) { it(`Should add the ${snippet} snippet to defs`, () => { - expect(pattern.svg.defs.indexOf(``)).to.not.equal(-1) + expect(pattern.svg.defs.get(snippet)).to.not.equal(false) }) } diff --git a/plugins/plugin-annotations/tests/logo.test.mjs b/plugins/plugin-annotations/tests/logo.test.mjs index 440a8f3bc9d..3e0c6bc5c09 100644 --- a/plugins/plugin-annotations/tests/logo.test.mjs +++ b/plugins/plugin-annotations/tests/logo.test.mjs @@ -9,8 +9,6 @@ describe('Logo Plugin Tests', () => { const Pattern = new Design() const pattern = new Pattern().use(annotationsPlugin) pattern.draft().render() - expect(pattern.svg.defs).to.contain( - '