├── .gitignore
├── README.md
├── index.html
├── src
└── yome.js
└── style.css
/.gitignore:
--------------------------------------------------------------------------------
1 | .module-cache
2 | node_modules/
3 | \#*
4 | \.\#*
5 | build/*
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Functional React Yome Widget
2 |
3 | I wrote this widget to demonstrate how to write straightforward
4 | functional code with [React.js](https://facebook.github.io/react/).
5 |
6 | The the blogpost. ;; not published link needed
7 |
8 | If you would like to experiment with this code you will need to
9 | install [Babel](http://babeljs.io/).
10 |
11 | ```
12 | npm install -g babel
13 | ```
14 |
15 | To auto-compile this code each time you save the `yome.js` file you
16 | will need to run babel like so:
17 |
18 | ```
19 | babel src/ -w --out-dir build/
20 | ```
21 |
22 | The top of the `yome.js` file has a little auto-loading script that
23 | will continuously reload the code. This allows you to change the code
24 | and experiment with code and see your changes **without reloading the
25 | browser**.
26 |
27 | Have fun!!
28 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/yome.js:
--------------------------------------------------------------------------------
1 | var Reloader = Reloader || {};
2 |
3 | Reloader.reloadFile = (path) => {
4 | var x = document.createElement("script");
5 | x.setAttribute("src",path + "?rel=" + (new Date().getTime()));
6 | document.body.appendChild(x);
7 | setTimeout(function(){ document.body.removeChild(x);}, 500);
8 | }
9 |
10 | Reloader.startReloading = (files) => {
11 | setTimeout(function() {
12 | console.log("--- reloading ---");
13 | files.map(Reloader.reloadFile);
14 | }, 1000);
15 | }
16 |
17 | Reloader.startReloading(["build/yome.js"])
18 |
19 | function l(x) { console.log(x); return x; }
20 |
21 | var Yome = Yome || {};
22 |
23 | Yome.initialState = () => {
24 | return { sides: [1,2,3,4,5,6,7,8].map( () => new Object() ) }
25 | }
26 |
27 | Yome.state = Yome.state || Yome.initialState();
28 | //l(Yome.state)
29 |
30 | Yome.sideCount = (st) => st.sides.length
31 | //l(Yome.sideCount(Yome.state))
32 |
33 | Yome.sliceTheta = (st) => 2 * Math.PI / Yome.sideCount(st)
34 | //l(Yome.sliceTheta(Yome.state))
35 |
36 |
37 | Yome.rotate = (theta, point) => {
38 | const sint = Math.sin(theta), cost = Math.cos(theta);
39 | return { x: (point.x * cost) - (point.y * sint),
40 | y: (point.x * sint) + (point.y * cost) };
41 | }
42 | //l(Yome.rotate(Math.PI, {x: 0, y: 1}));
43 |
44 | Yome.radialPoint = (radius, theta) =>
45 | Yome.rotate(theta, {x: 0, y: radius})
46 | //l(Yome.radialPoint(100, Math.PI));
47 |
48 | Yome.sidePoints = (st) =>
49 | st.sides.map((_,i) => Yome.radialPoint(180, i * Yome.sliceTheta(st)))
50 | //l(Yome.sidePoints(Yome.initialState()))
51 |
52 | Yome.pointsToPointsString = (points) =>
53 | points.map(p => p.x + "," + p.y).join(" ")
54 | //l(Yome.pointsToPointsString(Yome.sidePoints(Yome.initialState())))
55 |
56 | Yome.drawWalls = (state) =>
57 |
58 |
59 |
60 | Yome.svgWorld = (children) =>
61 |
63 | {children}
64 |
65 |
66 | Yome.playArea = (children) =>
67 | React.render(Yome.svgWorld(children), document.getElementById("playarea"))
68 |
69 | Yome.clearPlayArea = () =>
70 | React.unmountComponentAtNode(document.getElementById("playarea"))
71 |
72 | //Yome.playArea(Yome.drawWalls({sides: [1,2,3,4,5,6]}))
73 | //Yome.playArea(Yome.drawWalls({sides: [1,2,3,4,5,6,7]}))
74 | //Yome.playArea(Yome.drawWalls({sides: [1,2,3,4,5,6,7,8]}))
75 |
76 | //Yome.clearPlayArea()
77 |
78 | Yome.windowPoints = (st) => {
79 | const theta = Yome.sliceTheta(st),
80 | indent = theta / 6;
81 | return [Yome.radialPoint(160, indent),
82 | Yome.radialPoint(160, theta - indent),
83 | Yome.radialPoint(100, theta / 2)];
84 | }
85 | //l(Yome.windowPoints(Yome.initialState()))
86 |
87 | Yome.drawWindow = (st) =>
88 |
89 |
90 |
91 | //Yome.playArea({Yome.drawWindow(Yome.initialState())}
92 | // {Yome.drawWalls(Yome.initialState())} )
93 |
94 | Yome.doorPoints = (st) => {
95 | const indent = Yome.sliceTheta(st) / 8;
96 | return [Yome.radialPoint(165, indent ),
97 | Yome.radialPoint(165, -indent),
98 | Yome.radialPoint(90, -indent),
99 | Yome.radialPoint(90, indent)];
100 | }
101 |
102 | Yome.drawDoor = (st) =>
103 |
104 |
105 |
106 | //Yome.playArea({Yome.drawDoor(Yome.state)}
107 | // {Yome.drawWindow(Yome.state)}
108 | // {Yome.drawWalls(Yome.state)} )
109 |
110 | Yome.drawLine = (line) =>
111 |
113 |
114 |
115 | Yome.drawZipDoor = (st) => {
116 | const theta = Yome.sliceTheta(st),
117 | indent = 0.15 * (theta / 6),
118 | lines = [0,1,2,3,4,5,6,7,8].map((x) => {
119 | const dist = 170 - (10 * x);
120 | return {start: Yome.radialPoint(dist, -indent),
121 | end: Yome.radialPoint(dist, indent)}});
122 | lines.push({start: Yome.radialPoint(180, 0),
123 | end: Yome.radialPoint(90, 0)});
124 | return {lines.map(Yome.drawLine)} ;
125 | }
126 |
127 | //Yome.playArea({Yome.drawZipDoor(Yome.state)}
128 | // {Yome.drawWalls(Yome.state)} )
129 |
130 | Yome.drawStoveVent = (st) => {
131 | const theta = Yome.sliceTheta(st),
132 | point = Yome.radialPoint(155, 0);
133 | return
135 | }
136 |
137 | //Yome.playArea({Yome.drawStoveVent(Yome.state)}
138 | // {Yome.drawWalls(Yome.state)} )
139 |
140 | Yome.itemRenderDispatch = {
141 | "window": Yome.drawWindow,
142 | "door-frame": Yome.drawDoor,
143 | "zip-door": Yome.drawZipDoor,
144 | "stove-vent": Yome.drawStoveVent,
145 | }
146 |
147 | Yome.itemRender = (type, st) =>
148 | (Yome.itemRenderDispatch[type] || (x => null))(st)
149 |
150 | Yome.exampleData = ((state)=>{
151 | state.sides[0].face = "window"
152 | state.sides[0].corner = "zip-door"
153 | state.sides[3].face = "window"
154 | state.sides[5].corner = "door-frame"
155 | state.sides[5].face = "window"
156 | state.sides[7].corner = "stove-vent"
157 | return state
158 | })(Yome.initialState())
159 |
160 | //l(JSON.stringify(Yome.exampleData))
161 |
162 | Yome.sliceDeg = (st) => 360 / Yome.sideCount(st)
163 |
164 | Yome.sideSlice = (st, i) => {
165 | const side = st.sides[i];
166 | if(side.corner || side.face)
167 | return
168 | {Yome.itemRender(side.corner, st)}
169 | {Yome.itemRender(side.face, st)}
170 |
171 | }
172 |
173 | //Yome.playArea(Yome.sideSlice(Yome.exampleData, 5))
174 | //Yome.playArea(Yome.sideSlice(Yome.exampleData, 0))
175 |
176 | Yome.drawYome = (st) =>
177 |
178 | { Yome.drawWalls(st) }
179 | { st.sides.map((side, i) => Yome.sideSlice(st,i)) }
180 |
181 |
182 | //Yome.playArea(Yome.drawYome(Yome.exampleData))
183 | //Yome.clearPlayArea()
184 |
185 | //side effecting
186 | Yome.eventHandler = (f) =>
187 | (e => {e.preventDefault(); f(e.target.value); Yome.render()})
188 |
189 | //side effecting
190 | Yome.changeSideCount = (new_count) => {
191 | let nArray = Array.apply(null, Array(parseInt(new_count)));
192 | Yome.state.sides = nArray.map((_,i) => Yome.state.sides[i] || {});
193 | }
194 | //Yome.changeSideCount(6)
195 | //Yome.changeSideCount(7)
196 | //Yome.changeSideCount(8)
197 |
198 | Yome.sideOptions = () =>
199 | ["HexaYome", "SeptaYome","OctaYome"].map(
200 | (l, v) => {l} )
201 |
202 | Yome.sideCountInput = st =>
203 |
204 | Size of Yome
205 |
207 | { Yome.sideOptions() }
208 |
209 |
210 | //React.render(Yome.sideCountInput(Yome.state),
211 | // document.getElementById("playarea"))
212 | //Yome.clearPlayArea()
213 |
214 | Yome.worldPosition = (point) => ({ x: point.x + 250, y: point.y + 250})
215 |
216 | Yome.addRemoveWindow = (i) =>
217 | (_) => {
218 | const side = Yome.state.sides[i];
219 | side.face = (!side.face ? "window" : null);
220 | }
221 |
222 | Yome.windowControl = (st, side, i) => {
223 | let theta = Yome.sliceTheta(st) * (i + 1),
224 | pos = Yome.worldPosition(Yome.radialPoint(200, theta)),
225 | add = !side.face;
226 | return
235 | }
236 |
237 | Yome.windowControls = (st) =>
238 | st.sides.map((side, i) => Yome.windowControl(st, side, i))
239 |
240 | // --- Corner Controls
241 |
242 | // SIDE EFFECT
243 | Yome.addRemoveCornerItem = (type, side) =>
244 | (_) => side.corner = (side.corner ? null : type)
245 |
246 | Yome.cornerControlStateClass = (type, corner_type) =>
247 | ((! corner_type) && "add") || ((corner_type == type) && "remove") || "hidden"
248 |
249 | Yome.cornerControlLink = (type, side) =>
250 |
253 | { (side.corner ? "- " : "+ ") + type }
254 |
255 |
256 | Yome.cornerControlLinks = (side, i) =>
257 | ["stove-vent", "zip-door", "door-frame"].map(
258 | (t) => Yome.cornerControlLink(t, side))
259 |
260 | Yome.cornerControl = (st, side, i) => {
261 | let theta = Yome.sliceTheta(st) * (i + 0.5),
262 | pos = Yome.worldPosition(Yome.radialPoint(221, theta));
263 | return
264 |
265 | { Yome.cornerControlLinks(side, i) }
266 |
267 |
268 | }
269 |
270 | Yome.cornerControls = (st) =>
271 | st.sides.map((side, i) => Yome.cornerControl(st, side, i))
272 |
273 | // --- Add new code above this line ---
274 |
275 | Yome.widget = (st) =>
276 |
277 | { Yome.sideCountInput(st) }
278 |
279 | { Yome.windowControls(st) }
280 | { Yome.cornerControls(st) }
281 | { Yome.svgWorld(Yome.drawYome(st)) }
282 |
283 |
284 |
285 | Yome.render = () =>
286 | React.render(Yome.widget(Yome.state), document.getElementById('app'))
287 |
288 | Yome.render();
289 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: rgb(24,26,38);
3 | color: white;
4 | font-family: sans-serif;
5 | }
6 |
7 | svg line, svg polygon, svg ellipse {
8 | stroke: #2997ab;
9 | stroke-width: 2;
10 | fill: transparent;
11 | -webkit-animation: appear 0.7s;
12 | }
13 |
14 | .yome-widget-body { position: relative; }
15 | .control-holder { position: absolute; }
16 |
17 | .window-control-offset {
18 | position: relative;
19 | top: -7px;
20 | left: -25px;
21 | }
22 |
23 | .corner-control-offset {
24 | position: relative;
25 | top: -21px;
26 | left: -35px;
27 | }
28 |
29 | a {
30 | color: white;
31 | text-decoration: none;
32 | display: block;
33 | font-size: 12px;
34 | white-space: nowrap;
35 | }
36 |
37 | a.remove { color: rgb(239, 131, 75);}
38 | a.hidden { visibility: hidden;}
39 |
40 | @-webkit-keyframes appear {
41 | 0% { opacity: 0; }
42 | 100% { opacity: 1;}
43 | }
44 |
--------------------------------------------------------------------------------