flow: color, dialog, Evolved
This commit is contained in:
parent
d69f1418e8
commit
15c9851f2a
1
packages/color/.eslintrc.js
Normal file
1
packages/color/.eslintrc.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('../../config/.eslintrc');
|
7
packages/color/.gitignore
vendored
Normal file
7
packages/color/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
**/*.js
|
||||||
|
**/*.map
|
||||||
|
!/.*
|
||||||
|
!/postcss.config.js
|
||||||
|
!/webpack.config.js
|
||||||
|
!src/**/*.js
|
||||||
|
!/test/**/*.js
|
1
packages/color/.neutrinorc.js
Normal file
1
packages/color/.neutrinorc.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('../../config/.neutrinorc');
|
46
packages/color/package.json
Normal file
46
packages/color/package.json
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "@avocado/color",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"author": "cha0s",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "NODE_PATH=./node_modules webpack --mode production",
|
||||||
|
"clean": "rm -rf yarn.lock node_modules $(node -e \"process.stdout.write(require('./package.json').files.filter((file) => {const parts = file.split('/'); return 1 === parts.length || 'test' !== parts[0];}).join(' '));\") && yarn",
|
||||||
|
"forcepub": "npm unpublish --force $(node -e 'const {name, version} = require(`./package.json`); process.stdout.write(`${name}@${version}`)') && npm publish",
|
||||||
|
"lint": "NODE_PATH=./node_modules eslint --format codeframe --ext mjs,js .",
|
||||||
|
"test": "yarn --silent run build --display none && mocha --colors test.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"index.js.map",
|
||||||
|
"test.js",
|
||||||
|
"test.js.map"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@avocado/behavior": "^2.0.0",
|
||||||
|
"@avocado/traits": "^2.0.0",
|
||||||
|
"@latus/core": "^2.0.0",
|
||||||
|
"debug": "4.3.1",
|
||||||
|
"lodash.get": "^4.4.2",
|
||||||
|
"lodash.set": "^4.3.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@neutrinojs/airbnb": "^9.4.0",
|
||||||
|
"@neutrinojs/banner": "^9.4.0",
|
||||||
|
"@neutrinojs/copy": "^9.4.0",
|
||||||
|
"@neutrinojs/mocha": "^9.4.0",
|
||||||
|
"@neutrinojs/react": "^9.4.0",
|
||||||
|
"autoprefixer": "^9.8.6",
|
||||||
|
"chai": "4.2.0",
|
||||||
|
"eslint": "^7",
|
||||||
|
"eslint-import-resolver-webpack": "0.13.0",
|
||||||
|
"glob": "7.1.6",
|
||||||
|
"mocha": "^8",
|
||||||
|
"neutrino": "^9.4.0",
|
||||||
|
"source-map-support": "0.5.19",
|
||||||
|
"webpack": "^4",
|
||||||
|
"webpack-cli": "^3",
|
||||||
|
"webpack-node-externals": "2.5.2"
|
||||||
|
}
|
||||||
|
}
|
6
packages/color/postcss.config.js
Normal file
6
packages/color/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/* eslint-disable global-require */
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('autoprefixer'),
|
||||||
|
],
|
||||||
|
};
|
27
packages/color/src/dialog-functions.js
Normal file
27
packages/color/src/dialog-functions.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import get from 'lodash.get';
|
||||||
|
import set from 'lodash.set';
|
||||||
|
|
||||||
|
import hsv2rgb from './hsv2rgb';
|
||||||
|
|
||||||
|
export default () => ({
|
||||||
|
rainbow: ({
|
||||||
|
active,
|
||||||
|
traits,
|
||||||
|
i,
|
||||||
|
length,
|
||||||
|
}) => {
|
||||||
|
const [red, green, blue] = hsv2rgb([i / length, 1, 1]);
|
||||||
|
set(traits, 'Colorized.state', {red, green, blue});
|
||||||
|
if (active) {
|
||||||
|
const lfo = get(traits, 'Evolving.params.lfo', {});
|
||||||
|
lfo.hue = {
|
||||||
|
frequency: 1,
|
||||||
|
location: ((i % length) / length),
|
||||||
|
magnitude: 1,
|
||||||
|
modulators: ['Sawtooth'],
|
||||||
|
};
|
||||||
|
set(traits, 'Evolving.params.lfo', lfo);
|
||||||
|
}
|
||||||
|
return traits;
|
||||||
|
},
|
||||||
|
});
|
23
packages/color/src/hsv2rgb.js
Normal file
23
packages/color/src/hsv2rgb.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
export default ([h, s, v]) => {
|
||||||
|
let r;
|
||||||
|
let g;
|
||||||
|
let b;
|
||||||
|
const i = Math.floor(h * 6);
|
||||||
|
const f = h * 6 - i;
|
||||||
|
const p = v * (1 - s);
|
||||||
|
const q = v * (1 - f * s);
|
||||||
|
const t = v * (1 - (1 - f) * s);
|
||||||
|
switch (i % 6) {
|
||||||
|
default: [r, g, b] = [v, t, p]; break;
|
||||||
|
case 1: [r, g, b] = [q, v, p]; break;
|
||||||
|
case 2: [r, g, b] = [p, v, t]; break;
|
||||||
|
case 3: [r, g, b] = [p, q, v]; break;
|
||||||
|
case 4: [r, g, b] = [t, p, v]; break;
|
||||||
|
case 5: [r, g, b] = [v, p, q]; break;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
Math.round(r * 255),
|
||||||
|
Math.round(g * 255),
|
||||||
|
Math.round(b * 255),
|
||||||
|
];
|
||||||
|
};
|
12
packages/color/src/index.js
Normal file
12
packages/color/src/index.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import {gatherWithLatus} from '@latus/core';
|
||||||
|
|
||||||
|
import dialogFunctions from './dialog-functions';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
hooks: {
|
||||||
|
'@avocado/dialog/functions': dialogFunctions,
|
||||||
|
'@avocado/traits': gatherWithLatus(
|
||||||
|
require.context('./traits', false, /\.js$/),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
36
packages/color/src/rgb2hsv.js
Normal file
36
packages/color/src/rgb2hsv.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
export default ([r, g, b]) => {
|
||||||
|
const rabs = r / 255;
|
||||||
|
const gabs = g / 255;
|
||||||
|
const babs = b / 255;
|
||||||
|
const v = Math.max(rabs, gabs, babs);
|
||||||
|
const diff = v - Math.min(rabs, gabs, babs);
|
||||||
|
const diffc = (c) => (v - c) / 6 / diff + 1 / 2;
|
||||||
|
let h;
|
||||||
|
let s;
|
||||||
|
if (0 === diff) {
|
||||||
|
h = 0;
|
||||||
|
s = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s = diff / v;
|
||||||
|
const rr = diffc(rabs);
|
||||||
|
const gg = diffc(gabs);
|
||||||
|
const bb = diffc(babs);
|
||||||
|
if (rabs === v) {
|
||||||
|
h = bb - gg;
|
||||||
|
}
|
||||||
|
else if (gabs === v) {
|
||||||
|
h = (1 / 3) + rr - bb;
|
||||||
|
}
|
||||||
|
else if (babs === v) {
|
||||||
|
h = (2 / 3) + gg - rr;
|
||||||
|
}
|
||||||
|
if (h < 0) {
|
||||||
|
h += 1;
|
||||||
|
}
|
||||||
|
else if (h > 1) {
|
||||||
|
h -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [h, s, v];
|
||||||
|
};
|
|
@ -1,7 +1,8 @@
|
||||||
import {StateProperty, Trait} from '@avocado/traits';
|
import {StateProperty, Trait} from '@avocado/traits';
|
||||||
import {compose} from '@latus/core';
|
import {compose} from '@latus/core';
|
||||||
|
|
||||||
import Color from '../color';
|
import hsv2rgb from '../hsv2rgb';
|
||||||
|
import rgb2hsv from '../rgb2hsv';
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
StateProperty('red', {
|
StateProperty('red', {
|
||||||
|
@ -15,7 +16,7 @@ const decorate = compose(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export default () => class Colored extends decorate(Trait) {
|
export default () => class Colorized extends decorate(Trait) {
|
||||||
|
|
||||||
static children() {
|
static children() {
|
||||||
return {
|
return {
|
||||||
|
@ -69,7 +70,7 @@ export default () => class Colored extends decorate(Trait) {
|
||||||
}
|
}
|
||||||
|
|
||||||
get hue() {
|
get hue() {
|
||||||
return Color.rgb2hsv(this.red, this.green, this.blue)[0];
|
return rgb2hsv([this.red, this.green, this.blue])[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
set hue(hue) {
|
set hue(hue) {
|
||||||
|
@ -87,7 +88,7 @@ export default () => class Colored extends decorate(Trait) {
|
||||||
}
|
}
|
||||||
|
|
||||||
get saturation() {
|
get saturation() {
|
||||||
return Color.rgb2hsv(this.red, this.green, this.blue)[1];
|
return rgb2hsv([this.red, this.green, this.blue])[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
set saturation(saturation) {
|
set saturation(saturation) {
|
||||||
|
@ -95,7 +96,7 @@ export default () => class Colored extends decorate(Trait) {
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return Color.rgb2hsv(this.red, this.green, this.blue)[2];
|
return rgb2hsv([this.red, this.green, this.blue])[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
set value(value) {
|
set value(value) {
|
||||||
|
@ -103,19 +104,19 @@ export default () => class Colored extends decorate(Trait) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapHsvSet(i, nv) {
|
wrapHsvSet(i, nv) {
|
||||||
const hsv = Color.rgb2hsv(this.red, this.green, this.blue);
|
const hsv = rgb2hsv([this.red, this.green, this.blue]);
|
||||||
const keys = ['hue', 'saturation', 'value'];
|
const keys = ['hue', 'saturation', 'value'];
|
||||||
if (nv !== hsv[i]) {
|
if (nv !== hsv[i]) {
|
||||||
hsv[i] = nv;
|
hsv[i] = nv;
|
||||||
[this.red, this.green, this.blue] = Color.hsv2rgb(...hsv);
|
[this.red, this.green, this.blue] = hsv2rgb(hsv);
|
||||||
this.entity.emit(`${keys[i]}Changed`);
|
this.entity.emit(`${keys[i]}Changed`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapRgbSet(fn) {
|
wrapRgbSet(fn) {
|
||||||
const o = Color.rgb2hsv(this.red, this.green, this.blue);
|
const o = rgb2hsv([this.red, this.green, this.blue]);
|
||||||
fn();
|
fn();
|
||||||
const n = Color.rgb2hsv(this.red, this.green, this.blue);
|
const n = rgb2hsv([this.red, this.green, this.blue]);
|
||||||
['hue', 'saturation', 'value'].forEach((key, i) => {
|
['hue', 'saturation', 'value'].forEach((key, i) => {
|
||||||
if (n[i] !== o[i]) {
|
if (n[i] !== o[i]) {
|
||||||
this.entity.emit(`${key}Changed`);
|
this.entity.emit(`${key}Changed`);
|
5
packages/color/test/exists.js
Normal file
5
packages/color/test/exists.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import {expect} from 'chai';
|
||||||
|
|
||||||
|
it('exists', () => {
|
||||||
|
expect(true).to.be.true;
|
||||||
|
});
|
3
packages/color/webpack.config.js
Normal file
3
packages/color/webpack.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const neutrino = require('neutrino');
|
||||||
|
|
||||||
|
module.exports = neutrino(require(`${__dirname}/.neutrinorc`)).webpack();
|
8518
packages/color/yarn.lock
Normal file
8518
packages/color/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
1
packages/dialog/.eslintrc.js
Normal file
1
packages/dialog/.eslintrc.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('../../config/.eslintrc');
|
7
packages/dialog/.gitignore
vendored
Normal file
7
packages/dialog/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
**/*.js
|
||||||
|
**/*.map
|
||||||
|
!/.*
|
||||||
|
!/postcss.config.js
|
||||||
|
!/webpack.config.js
|
||||||
|
!src/**/*.js
|
||||||
|
!/test/**/*.js
|
1
packages/dialog/.neutrinorc.js
Normal file
1
packages/dialog/.neutrinorc.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('../../config/.neutrinorc');
|
49
packages/dialog/package.json
Normal file
49
packages/dialog/package.json
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "@avocado/dialog",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"author": "cha0s",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "NODE_PATH=./node_modules webpack --mode production",
|
||||||
|
"clean": "rm -rf yarn.lock node_modules $(node -e \"process.stdout.write(require('./package.json').files.filter((file) => {const parts = file.split('/'); return 1 === parts.length || 'test' !== parts[0];}).join(' '));\") && yarn",
|
||||||
|
"forcepub": "npm unpublish --force $(node -e 'const {name, version} = require(`./package.json`); process.stdout.write(`${name}@${version}`)') && npm publish",
|
||||||
|
"lint": "NODE_PATH=./node_modules eslint --format codeframe --ext mjs,js .",
|
||||||
|
"test": "yarn --silent run build --display none && mocha --colors test.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"index.js.map",
|
||||||
|
"test.js",
|
||||||
|
"test.js.map"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@avocado/traits": "^2.0.0",
|
||||||
|
"@latus/core": "^2.0.0",
|
||||||
|
"debug": "4.3.1",
|
||||||
|
"lodash.get": "^4.4.2",
|
||||||
|
"lodash.set": "^4.3.2",
|
||||||
|
"remark-mdx": "2.0.0-next.8",
|
||||||
|
"remark-parse": "8.0.2",
|
||||||
|
"unified": "^9.2.0",
|
||||||
|
"unist-util-visit-parents": "^3.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@neutrinojs/airbnb": "^9.4.0",
|
||||||
|
"@neutrinojs/banner": "^9.4.0",
|
||||||
|
"@neutrinojs/copy": "^9.4.0",
|
||||||
|
"@neutrinojs/mocha": "^9.4.0",
|
||||||
|
"@neutrinojs/react": "^9.4.0",
|
||||||
|
"autoprefixer": "^9.8.6",
|
||||||
|
"chai": "4.2.0",
|
||||||
|
"eslint": "^7",
|
||||||
|
"eslint-import-resolver-webpack": "0.13.0",
|
||||||
|
"glob": "7.1.6",
|
||||||
|
"mocha": "^8",
|
||||||
|
"neutrino": "^9.4.0",
|
||||||
|
"source-map-support": "0.5.19",
|
||||||
|
"webpack": "^4",
|
||||||
|
"webpack-cli": "^3",
|
||||||
|
"webpack-node-externals": "2.5.2"
|
||||||
|
}
|
||||||
|
}
|
6
packages/dialog/postcss.config.js
Normal file
6
packages/dialog/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/* eslint-disable global-require */
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('autoprefixer'),
|
||||||
|
],
|
||||||
|
};
|
56
packages/dialog/src/dialog-functions.js
Normal file
56
packages/dialog/src/dialog-functions.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import get from 'lodash.get';
|
||||||
|
import set from 'lodash.set';
|
||||||
|
|
||||||
|
export default () => ({
|
||||||
|
rate: ({
|
||||||
|
frequency = 0.05,
|
||||||
|
traits,
|
||||||
|
}) => {
|
||||||
|
set(traits, 'DialogText.state.rate', parseInt(frequency, 10));
|
||||||
|
return traits;
|
||||||
|
},
|
||||||
|
shake: ({
|
||||||
|
frequency = 1,
|
||||||
|
magnitude = 3,
|
||||||
|
traits,
|
||||||
|
i,
|
||||||
|
length,
|
||||||
|
}) => {
|
||||||
|
const lfo = get(traits, 'Evolving.params.lfo', {});
|
||||||
|
lfo.x = {
|
||||||
|
frequency,
|
||||||
|
location: ((i % length) / length),
|
||||||
|
magnitude: magnitude / 2,
|
||||||
|
median: 0,
|
||||||
|
modulators: ['Random'],
|
||||||
|
};
|
||||||
|
lfo.y = {
|
||||||
|
frequency,
|
||||||
|
location: ((i % length) / length),
|
||||||
|
magnitude,
|
||||||
|
median: 0,
|
||||||
|
modulators: ['Random'],
|
||||||
|
};
|
||||||
|
set(traits, 'Evolving.params.lfo', lfo);
|
||||||
|
return traits;
|
||||||
|
},
|
||||||
|
wave: ({
|
||||||
|
frequency = 1,
|
||||||
|
magnitude = 3,
|
||||||
|
modulator = 'Sine',
|
||||||
|
traits,
|
||||||
|
i,
|
||||||
|
length,
|
||||||
|
}) => {
|
||||||
|
const lfo = get(traits, 'Evolving.params.lfo', {});
|
||||||
|
lfo.y = {
|
||||||
|
frequency,
|
||||||
|
location: ((i % length) / length),
|
||||||
|
magnitude,
|
||||||
|
median: 0,
|
||||||
|
modulators: [modulator],
|
||||||
|
};
|
||||||
|
set(traits, 'Evolving.params.lfo', lfo);
|
||||||
|
return traits;
|
||||||
|
},
|
||||||
|
});
|
28
packages/dialog/src/index.js
Normal file
28
packages/dialog/src/index.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import {gatherWithLatus} from '@latus/core';
|
||||||
|
|
||||||
|
import dialogFunctions from './dialog-functions';
|
||||||
|
import parse from './parser';
|
||||||
|
import wordize from './wordize';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
hooks: {
|
||||||
|
'@avocado/dialog/functions': dialogFunctions,
|
||||||
|
'@avocado/traits': gatherWithLatus(
|
||||||
|
require.context('./traits', false, /\.js$/),
|
||||||
|
),
|
||||||
|
'@latus/core/starting': (latus) => {
|
||||||
|
const dialogFunctions = latus.invokeReduce('@avocado/dialog/functions');
|
||||||
|
const resolver = (type) => (dialogFunctions[type] ? dialogFunctions[type] : (i) => i);
|
||||||
|
latus.set('%dialogParser', async (text) => {
|
||||||
|
const {Entity} = latus.get('%resources');
|
||||||
|
const letters = await Promise.all(
|
||||||
|
parse(text, resolver).map((letter) => Entity.load({traits: letter})),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
letters,
|
||||||
|
words: await wordize(letters, latus),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
109
packages/dialog/src/parser.js
Normal file
109
packages/dialog/src/parser.js
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import get from 'lodash.get';
|
||||||
|
import set from 'lodash.set';
|
||||||
|
import mdx from 'remark-mdx';
|
||||||
|
import parse from 'remark-parse';
|
||||||
|
import unified from 'unified';
|
||||||
|
import visit from 'unist-util-visit-parents';
|
||||||
|
|
||||||
|
const letterCount = (node) => {
|
||||||
|
if ('text' === node.type) {
|
||||||
|
return node.value.length;
|
||||||
|
}
|
||||||
|
return node.children.length > 0
|
||||||
|
? node.children.reduce((count, child) => count + letterCount(child), 0)
|
||||||
|
: 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const decorateWithLetters = (resolver) => (node, ancestors) => {
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
if ('text' === node.type) {
|
||||||
|
const letters = node.value
|
||||||
|
.replace(/\s/g, '\u00A0')
|
||||||
|
.split('');
|
||||||
|
node.letters = letters.map((letter) => (
|
||||||
|
ancestors.reduce(
|
||||||
|
(traits, ancestor) => ancestor.traits(traits),
|
||||||
|
{
|
||||||
|
DomNode: {
|
||||||
|
params: {
|
||||||
|
style: {
|
||||||
|
display: 'inline-block',
|
||||||
|
position: 'relative',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DomText: {
|
||||||
|
params: {
|
||||||
|
centered: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Textual: {
|
||||||
|
state: {
|
||||||
|
text: letter,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Visible: {
|
||||||
|
state: {
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node.traits = (traits) => traits;
|
||||||
|
if ('emphasis' === node.type) {
|
||||||
|
node.traits = (traits) => {
|
||||||
|
const style = get(traits, 'DomNode.params.style', {});
|
||||||
|
style.fontStyle = 'italic';
|
||||||
|
set(traits, 'DomNode.params.style', style);
|
||||||
|
return traits;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ('strong' === node.type) {
|
||||||
|
node.traits = (traits) => {
|
||||||
|
const style = get(traits, 'DomNode.params.style', {});
|
||||||
|
style.fontWeight = 'bold';
|
||||||
|
set(traits, 'DomNode.params.style', style);
|
||||||
|
return traits;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
'mdxBlockElement' === node.type
|
||||||
|
|| 'mdxSpanElement' === node.type
|
||||||
|
) {
|
||||||
|
const {attributes, name} = node;
|
||||||
|
const length = letterCount(node);
|
||||||
|
let i = 0;
|
||||||
|
node.traits = (traits) => {
|
||||||
|
const args = attributes.reduce((r, {name, value}) => ({
|
||||||
|
...r,
|
||||||
|
[name]: (value?.value ? value.value : value) || true,
|
||||||
|
}), {});
|
||||||
|
return resolver(name)({
|
||||||
|
length,
|
||||||
|
...args,
|
||||||
|
i: i++,
|
||||||
|
traits,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/* eslint-enable no-param-reassign */
|
||||||
|
};
|
||||||
|
|
||||||
|
const lettersFor = (node) => {
|
||||||
|
if (node.children) {
|
||||||
|
return node.children.reduce(
|
||||||
|
(r, child) => r.concat(...lettersFor(child)),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return node.letters || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (source, resolver) => {
|
||||||
|
const tree = unified().use(parse).use(mdx).parse(source);
|
||||||
|
visit(tree, decorateWithLetters(resolver));
|
||||||
|
return lettersFor(tree);
|
||||||
|
};
|
16
packages/dialog/src/traits/dialog-text.js
Normal file
16
packages/dialog/src/traits/dialog-text.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import {StateProperty, Trait} from '@avocado/traits';
|
||||||
|
import {compose} from '@latus/core';
|
||||||
|
|
||||||
|
const decorate = compose(
|
||||||
|
StateProperty('rate'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export default () => class DialogText extends decorate(Trait) {
|
||||||
|
|
||||||
|
static defaultState() {
|
||||||
|
return {
|
||||||
|
rate: 0.1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
90
packages/dialog/src/traits/dialog.js
Normal file
90
packages/dialog/src/traits/dialog.js
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import {Trait} from '@avocado/traits';
|
||||||
|
|
||||||
|
export default (latus) => class Dialog extends Trait {
|
||||||
|
|
||||||
|
#dialog;
|
||||||
|
|
||||||
|
#letters = [];
|
||||||
|
|
||||||
|
#nextLetter = 0;
|
||||||
|
|
||||||
|
#thisLetter = 0;
|
||||||
|
|
||||||
|
#words = [];
|
||||||
|
|
||||||
|
static defaultParams() {
|
||||||
|
return {
|
||||||
|
rate: 0.05,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static dependencies() {
|
||||||
|
return [
|
||||||
|
'Textual',
|
||||||
|
'DomNode',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
hooks() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
visibleAabbs: () => [-64, -64, 64, 64],
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(json) {
|
||||||
|
await super.load(json);
|
||||||
|
if ('client' === process.env.SIDE) {
|
||||||
|
this.entity.node.className = 'dialog';
|
||||||
|
const {text} = this.entity;
|
||||||
|
if (text) {
|
||||||
|
({
|
||||||
|
letters: this.#letters,
|
||||||
|
words: this.#words,
|
||||||
|
} = await latus.get('%dialogParser')(
|
||||||
|
`<rate frequency={${this.params.rate}}>${text}</rate>`,
|
||||||
|
));
|
||||||
|
if (this.#letters.length > 0) {
|
||||||
|
this.showThisLetter();
|
||||||
|
this.#words.forEach((word) => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
word.parentNode = this.entity.node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showThisLetter() {
|
||||||
|
const letter = this.#letters[this.#thisLetter];
|
||||||
|
this.#nextLetter += letter.rate;
|
||||||
|
letter.opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip() {
|
||||||
|
for (let i = 0; i < this.#letters.length; ++i) {
|
||||||
|
this.#letters[i].opacity = 1;
|
||||||
|
}
|
||||||
|
this.#nextLetter = Infinity;
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(elapsed) {
|
||||||
|
if ('client' === process.env.SIDE) {
|
||||||
|
this.#nextLetter -= elapsed;
|
||||||
|
if (this.#nextLetter <= 0) {
|
||||||
|
this.#thisLetter += 1;
|
||||||
|
if (this.#thisLetter >= this.#letters.length) {
|
||||||
|
this.skip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.showThisLetter();
|
||||||
|
}
|
||||||
|
for (let i = 0; i < this.#words.length; ++i) {
|
||||||
|
this.#words[i].tick(elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
45
packages/dialog/src/wordize.js
Normal file
45
packages/dialog/src/wordize.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
const createWord = async (letters, latus) => {
|
||||||
|
const {Entity} = latus.get('%resources');
|
||||||
|
const word = await Entity.load({
|
||||||
|
traits: {
|
||||||
|
DomNode: {
|
||||||
|
params: {
|
||||||
|
style: {
|
||||||
|
position: 'relative',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
for (let i = 0; i < letters.length; ++i) {
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
letters[i].parentNode = word.node;
|
||||||
|
letters[i].isBehaving = true;
|
||||||
|
/* eslint-enable no-param-reassign */
|
||||||
|
}
|
||||||
|
const {tick} = word;
|
||||||
|
word.tick = function T(elapsed) {
|
||||||
|
tick.call(this, elapsed);
|
||||||
|
for (let i = 0; i < letters.length; ++i) {
|
||||||
|
letters[i].tick(elapsed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return word;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async (letters, latus) => {
|
||||||
|
const words = [];
|
||||||
|
let word = [];
|
||||||
|
for (let i = 0; i < letters.length; i++) {
|
||||||
|
const {text} = letters[i];
|
||||||
|
word.push(letters[i]);
|
||||||
|
if (text.match(/[^a-zA-Z0-9_]/)) {
|
||||||
|
words.push(createWord(word, latus));
|
||||||
|
word = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (word.length > 0) {
|
||||||
|
words.push(createWord(word, latus));
|
||||||
|
}
|
||||||
|
return Promise.all(words);
|
||||||
|
};
|
5
packages/dialog/test/exists.js
Normal file
5
packages/dialog/test/exists.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import {expect} from 'chai';
|
||||||
|
|
||||||
|
it('exists', () => {
|
||||||
|
expect(true).to.be.true;
|
||||||
|
});
|
3
packages/dialog/webpack.config.js
Normal file
3
packages/dialog/webpack.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const neutrino = require('neutrino');
|
||||||
|
|
||||||
|
module.exports = neutrino(require(`${__dirname}/.neutrinorc`)).webpack();
|
8809
packages/dialog/yarn.lock
Normal file
8809
packages/dialog/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -31,6 +31,7 @@ export default () => class DomNode extends decorate(Trait) {
|
||||||
|
|
||||||
static dependencies() {
|
static dependencies() {
|
||||||
return [
|
return [
|
||||||
|
'Positioned',
|
||||||
'Visible',
|
'Visible',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,14 @@ export default () => class DomText extends Trait {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.#text = window.document.createElement('span');
|
if ('client' === process.env.SIDE) {
|
||||||
|
this.#text = window.document.createElement('span');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultParams() {
|
static defaultParams() {
|
||||||
return {
|
return {
|
||||||
centered: true,
|
centered: true,
|
||||||
textStyle: {},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,23 +48,28 @@ export default () => class DomText extends Trait {
|
||||||
|
|
||||||
async load(json) {
|
async load(json) {
|
||||||
await super.load(json);
|
await super.load(json);
|
||||||
|
if (!this.#text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const {text} = this.entity;
|
const {text} = this.entity;
|
||||||
this.#text.innerText = text;
|
this.#text.innerText = text;
|
||||||
Object.entries(this.params.textStyle).forEach(([key, value]) => {
|
|
||||||
this.#text.style[key] = value;
|
|
||||||
});
|
|
||||||
this.#text.style.position = 'relative';
|
this.#text.style.position = 'relative';
|
||||||
if (this.params.centered) {
|
const {fontSize} = this.params;
|
||||||
const {fontSize: fontSizeInPx = '16px'} = this.params.textStyle;
|
if (fontSize) {
|
||||||
const fontSize = parseInt(fontSizeInPx, 10);
|
this.#text.style.fontSize = `${fontSize}px`;
|
||||||
this.#text.style.left = `-${(fontSize / 2) * text.length}px`;
|
if (this.params.centered) {
|
||||||
this.#text.style.top = `-${(fontSize / 2)}px`;
|
this.#text.style.left = `-${(fontSize / 2) * text.length}px`;
|
||||||
|
this.#text.style.top = `-${(fontSize / 2)}px`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.entity.node.appendChild(this.#text);
|
this.entity.node.appendChild(this.#text);
|
||||||
}
|
}
|
||||||
|
|
||||||
onColorChanged() {
|
onColorChanged() {
|
||||||
if (this.entity.is('Colored')) {
|
if (!this.#text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.entity.is('Colorized')) {
|
||||||
const {red, green, blue} = this.entity;
|
const {red, green, blue} = this.entity;
|
||||||
this.#text.style.color = `rgb(${red}, ${green}, ${blue})`;
|
this.#text.style.color = `rgb(${red}, ${green}, ${blue})`;
|
||||||
}
|
}
|
||||||
|
|
25
packages/timing/src/traits/evolving.js
Normal file
25
packages/timing/src/traits/evolving.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import {Trait} from '@avocado/traits';
|
||||||
|
|
||||||
|
export default () => class Evolving extends Trait {
|
||||||
|
|
||||||
|
#evolving;
|
||||||
|
|
||||||
|
static defaultParams() {
|
||||||
|
return {
|
||||||
|
lfo: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(json) {
|
||||||
|
await super.load(json);
|
||||||
|
if (0 === Object.keys(this.params.lfo).length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#evolving = this.entity.lfo(this.params.lfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(elapsed) {
|
||||||
|
this.#evolving.tick(elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user