1
0
Fork 0

feat: Flat import of markdown repo

This is a flat (without history) import of (some of) the content
from our markdown module.

We've imported this without history because the repo contains our
blog posts and showcases posts content prior to porting them to strapi.

Since this contains many images, it would balloon the size of this repo
to import the full history.

Instead, please refer to the history of the (archived) markdown repo
at: https://github.com/freesewing/markdown
This commit is contained in:
Joost De Cock 2021-08-25 16:09:31 +02:00
parent 1671a896b5
commit b34a2ee2ed
6132 changed files with 244167 additions and 0 deletions

View file

@ -0,0 +1,65 @@
---
title: Conditionally loading build-time plugins
order: 30
---
You can choose to load your build-time plugin conditionally based on run-time data.
To do so, you need to create a `condition` method that will determine whether the
plugin will be loaded. This method receives the complete settings object and should
return `true` if the plugin is to be loaded, and `false` if it should not be loaded.
```js
const condition = settings => {
if (settings) {
// Remember, settings contains:
// settings.options => The user's options
// settings.measurements => The measurements
return true // Load the plugin
}
else return false // Do not load the plugin
}
```
You pass your plugin and condition method as a third parameter to the Design constructor
with the `plugin` and `condition` keys respectively.
Let's look at a complete example to illustrate this:
```js
import freesewing from '@freesewing/core'
import plugins from '@freesewing/plugin-bundle'
import myConditionalPlugin from '@freesewing/plugin-bust'
const myConditionalPluginCheck = (settings = false) =>
settings &&
settings.options &&
settings.options.draftForHighBust &&
settings.measurements.highBust
? true
: false
const Pattern = new freesewing.Design(
config,
plugins,
{
plugin: myConditionalPlugin,
condition: myConditionalPluginCheck
}
)
```
Our condition method will return `true` only if the following conditions are met:
- A `settings` object is passed into the method
- `settings.options` is _truthy_
- `settings.options.draftForHighBust` is _truthy_
- `settings.options.measurements.highBust` is _truthy_
This is a real-world example from our Teagan pattern. A t-shirt pattern that can be
drafted to the high bust (rather than the full chest circumference) if the user
choses so.
But that feat is handled auto-magically by `plugin-bust` which is a build-time plugin.
So whether to load this plugin or not hinges on the user settings, which is why we
load this plugin conditionally.

View file

@ -0,0 +1,25 @@
---
title: Plugin guide
order: 400
icons:
- logo
- plugin
for: developers
about: |
This guide shows you everything you need to know to understand plugins in FreeSewing, and create your own.
goals:
- Know about build-time plugins vs run-time plugins
- Understanding plugin structure
- Hooks and how to use them
- Using hooks without a plugin
- Using macros
---
Plugins allow you to extend FreeSewing.
We have [a list of available plugins](/reference/plugins/), but
if you can't find what you're looking for, you can write your own plugin.
We'll cover the following topics in this guide:
<ReadMore list />

View file

@ -0,0 +1,21 @@
---
title: Hooks
order: 60
---
A **hook** is a lifecycle event. The available hooks are:
- [preRender](/reference/hooks/prerender/): Called at the start of [`Pattern.render()`](/reference/api/pattern#render)
- [postRender](/reference/hooks/postrender/): Called at the end of [`Pattern.render()`](/reference/api/pattern#render)
- [insertText](/reference/hooks/inserttext/): Called when inserting text
- [preDraft](/reference/hooks/predraft/): Called at the start of [`Pattern.draft()`](/reference/api/pattern#draft)
- [postDraft](/reference/hooks/postdraft/): Called at the end of [`Pattern.draft()`](/reference/api/pattern#draft)
- [preSample](/reference/hooks/presample/): Called at the start of [`Pattern.sample()`](/reference/api/pattern#sample)
- [postSample](/reference/hooks/postsample/): Called at the end of [`Pattern.sample()`](/reference/api/pattern#sample)
You can register a method for a hook. When the hook is triggered, your method will be
called. It will receive two parameters:
- An object relevant to the hook. See the [hooks API reference](/reference/hooks/) for details.
- Data passed when the hook was registered (optional)

View file

@ -0,0 +1,27 @@
---
title: Loading build-time plugins
order: 20
---
Build-time plugins are loaded at build time, by passing them to
the [`freesewing.Design`](/reference/api/#design) constructor:
```js
import freesewing from "@freesewing/core"
import plugins from "@freesewing/plugin-bundle"
import config from "../config"
const Pattern = new freesewing.Design(config, plugins)
```
If you have multiple plugins to load, you can pass them as an array:
```js
import freesewing from "@freesewing/core"
import plugins from "@freesewing/plugin-bundle"
import gorePlugin from "@freesewing/plugin-gore"
import config from "../config"
const Pattern = new freesewing.Design(config, [plugins, gorePlugin] )
```

View file

@ -0,0 +1,26 @@
---
title: Loading run-time plugins
order: 40
---
Run-time plugin are loaded at run time, by passing them to the `use` method of
an instatiated pattern. That method is chainable, so if you have multiple plugins
you can just chain them together:
```js
import Aaron from "@freesewing/aaron";
import theme from "@freesewing/plugin-theme";
import i18n from "@freesewing/plugin-i18n";
const myAaron = new Aaron()
.use(theme)
.use(i18n)
```
<Tip>
Plugins that use only hooks are typically run-time plugins
</Tip>

View file

@ -0,0 +1,64 @@
---
title: Macros
order: 90
---
Plugin structure for macros is similar, with a few changes:
- Rather than the hook name, you provide the macro name (that you choose yourself)
- The context (`this`) of a macro method is **always** a [Part](/reference/api/part) object.
Apart from these, the structure is very similar:
```js
import {name, version} from '../package.json';
export default {
name,
version,
macros: {
box: function(so) {
this.points.boxTopLeft = so.anchor;
this.points.boxTopRight = so.anchor.shift(0, so.size);
this.points.boxBottomRight = this.points.boxTopRight.shift(-90, so.size);
this.points.boxBottomLeft = new this.Point(so.anchor.x, this.points.boxBottomRight.y);
this.paths.box = new this.Path()
.move(this.points.boxTopLeft)
.line(this.points.boxTopRight)
.line(this.points.boxBottomRight)
.line(this.points.boxBottomLeft)
.close()
.attr('class', 'box');
}
}
}
```
Did you figure out what this plugin does?
It provides a `box` macro that draws a box on our pattern in a given location with a give size.
We can use it like this:
```js
points.boxAnchor = new Point(100, 100);
macro('box', {
anchor: points.boxAnchor
size: 25
});
```
Obviously, you can expect to learn how to call a macro in its documentation,
rather than have to comb through its code.
<Note>
###### Macros take only 1 argument
When writing a macro, keep in mind that all information that needs to be passed
to a macro needs to be contained in a single argument.
Typically, you use a single plain object to configure the macro.
</Note>

View file

@ -0,0 +1,25 @@
---
title: Plugin structure
order: 50
---
Plugins can do two things:
- They can use hooks
- They can provide macros
Your plugin should export an object with the following structure:
```js
{
name: 'myPlugin',
version: '1.0.0',
hooks: {},
macros: {}
};
```
The `name` and `version` attributes are self-explanatory.
The [hooks](/guides/plugins/hooks/) and [macros](/guides/plugins/macros/) sections
explain the `hooks` and `macros` properties.

View file

@ -0,0 +1,38 @@
---
title: Types of plugins
order: 10
---
Plugins come in two flavours:
- [Build-time plugins](#build-time-plugins)
- [Run-time plugins](#run-time-plugins)
When writing a plugin, ask yourself whether it's a run-time or a build-time plugin.
And if the answer is both, please split them into two plugins.
## Build-time plugins
A plugin is a build-time plugin if it is required by the pattern at build-time.
In other words, the plugin is a dependency for the pattern, and if it's missing
the pattern won't load.
<Tip>
Our [plugin bundle](/reference/plugins/bundle/) bundles build-time plugins that are used in many patterns.
</Tip>
<Note>Plugins that provide a macro are typically build-time plugins</Note>
## Run-time plugins
A plugin is a run-time plugin if it can be added after instantiating your pattern.
Think of it as a plugin to be used in the front-end.
Run-time plugins are not a dependecy of the pattern. They just _add something_ to it.
Our [theme plugin](/reference/plugins/theme/) is a good example of a run-time plugin.
If it's missing, your pattern will still work, it just won't look pretty.

View file

@ -0,0 +1,26 @@
---
title: Using hooks more than once
order: 80
---
What if you want to attach more than one method to a hook?
You could spread them over seperate plugins, but there's a better way.
Rather than assigning a method to your hook, assign an array of methods like this:
```js
import myCoolMethod from './method-a';
import myEvenCoolerMethod from './method-b';
import {name, version} from '../package.json';
export default {
name,
version,
hooks: {
preRender: [
myCoolMethod,
myEvenCoolerMethod
]
}
}
```

View file

@ -0,0 +1,20 @@
---
title: Using hooks without a plugin
order: 85
---
You can attach a method to a hook at run-time without the need for a plugin
using the [Pattern.on()](/reference/api/pattern/on) method.
The method takes the hook name as its first argument, and the hook method as its second.
Below is an example:
```js
pattern.on('preRender', function(svg) {
svg.style += "svg { background: yellow;}";
});
```
Congratulations, you've just made your pattern yellow.

View file

@ -0,0 +1,59 @@
---
title: Using hooks
order: 70
---
For each hook, your plugin should provide a method that takes the relevant data
as its first argument. If data was passed when the hook was loaded, you will receive
that as the second object.
Remember that:
- The `insertText` hook will receive a locale and string and you should return a string.
- All other hooks receive an object. You don't need to return anything, but rather modify the object you receive.
Let's look at an example:
```js
import myStyle from './style';
import myDefs from './defs';
import {name, version} from '../package.json';
export default {
name,
version,
hooks: {
preRender: function(svg) {
if (svg.attributes.get("freesewing:plugin-"+name) === false) {
svg.style += myStyle;
svg.defs += myDefs;
svg.attributes.add("freesewing:plugin-"+name, version);
}
},
insertText: function(text) {
return text.toUpperCase();
}
}
}
```
This is a complete plugin, ready to be published on NPM. It uses two hooks:
- `preRender` : We add some style and defs to our SVG
- `insertText` : We transfer all text to UPPERCASE
<Note>
###### Note that we avoid running our hook twice
As you can see, the last thing we do in the `preRender` hook is set an attribute on
the SVG tag with the name and version of our plugin.
We check for this attribute when the `preRender` hook runs, thereby avoiding that
our styles and defs will be added twice.
It is good practice to wrap you hook methods in a call like this, because you have
no guarantee the user won't render your pattern more than once.
</Note>