Skip to content

Commit 1fd1637

Browse files
committed
feat: add ky-040 element #64
close #64
1 parent 9d7b3a3 commit 1fd1637

File tree

4 files changed

+260
-0
lines changed

4 files changed

+260
-0
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ export { SmallSoundSensorElement } from './small-sound-sensor-element';
4040
export { BigSoundSensorElement } from './big-sound-sensor-element';
4141
export { MPU6050Element } from './mpu6050-element';
4242
export { ESP32DevkitV1Element } from './esp32-devkit-v1-element';
43+
export { KY040Element } from './ky-040-element';

src/ky-040-element.stories.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { action } from '@storybook/addon-actions';
2+
import { html } from 'lit-html';
3+
import './ky-040-element';
4+
5+
export default {
6+
title: 'KY040',
7+
component: 'wokwi-ky-040',
8+
argTypes: {
9+
angle: { control: { type: 'range', min: 0, max: 360 } },
10+
},
11+
args: {
12+
angle: 0,
13+
},
14+
};
15+
16+
const Template = ({ angle }) =>
17+
html`<wokwi-ky-040
18+
.angle=${angle}
19+
@rotate-cw=${action('rotate-cw')}
20+
@rotate-ccw=${action('rotate-ccw')}
21+
@button-press=${action('button-press')}
22+
@button-release=${action('button-release')}
23+
>
24+
</wokwi-ky-040>`;
25+
26+
export const Default = Template.bind({});

src/ky-040-element.ts

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
import { css, customElement, html, LitElement, property } from 'lit-element';
2+
import { ElementPin } from '.';
3+
import { GND, VCC } from './pin';
4+
5+
@customElement('wokwi-ky-040')
6+
export class KY040Element extends LitElement {
7+
@property() angle = 0;
8+
@property() stepSize = 18;
9+
@property() private pressed = false;
10+
11+
static get styles() {
12+
return css`
13+
svg {
14+
user-select: none;
15+
}
16+
17+
.arrow {
18+
cursor: pointer;
19+
}
20+
21+
svg:hover .arrow {
22+
fill: #666;
23+
stroke: #666;
24+
stroke-width: 1.5px;
25+
transition: stroke-width 0.3s;
26+
}
27+
28+
svg:hover .arrow:hover {
29+
fill: black;
30+
}
31+
32+
svg:hover .handle:hover {
33+
stroke: #ccc;
34+
stroke-width: 1.5px;
35+
transition: stroke-width 0.3s;
36+
}
37+
38+
svg:hover .handle.active {
39+
fill: white;
40+
stroke: white;
41+
stroke-width: 1.5px;
42+
transition: stroke-width 0.3s;
43+
}
44+
45+
.handle {
46+
cursor: pointer;
47+
}
48+
`;
49+
}
50+
51+
readonly pinInfo: ElementPin[] = [
52+
{ name: 'CLK', y: 7.9, x: 116, number: 1, signals: [] },
53+
{ name: 'DT', y: 17.4, x: 116, number: 2, signals: [] },
54+
{ name: 'SW', y: 27, x: 116, number: 3, signals: [] },
55+
{ name: 'VCC', y: 36.3, x: 116, number: 4, signals: [VCC()] },
56+
{ name: 'GND', y: 45.5, x: 116, number: 5, signals: [GND()] },
57+
];
58+
59+
render() {
60+
return html`
61+
<svg
62+
width="30.815mm"
63+
height="18.63mm"
64+
version="1.1"
65+
viewBox="0 0 116 70.4"
66+
xmlns="http://www.w3.org/2000/svg"
67+
xmlns:xlink="http://www.w3.org/1999/xlink"
68+
>
69+
<defs>
70+
<linearGradient
71+
id="a"
72+
x1="158"
73+
x2="170"
74+
y1="86.5"
75+
y2="86.5"
76+
gradientTransform="translate(-75.1 -60.1)"
77+
gradientUnits="userSpaceOnUse"
78+
>
79+
<stop stop-color="#4d4d4d" offset="0" />
80+
<stop stop-color="#4d4d4d" stop-opacity="0" offset="1" />
81+
</linearGradient>
82+
</defs>
83+
84+
<!-- Board -->
85+
<path
86+
d="m0 0v70.4h99v-70.4zm18 56.5a6.5 6.5 0 0 1 6.5 6.5 6.5 6.5 0 0 1-6.5 6.5 6.5 6.5 0 0 1-6.5-6.5 6.5 6.5 0 0 1 6.5-6.5zm63.8 0.213a6.5 6.5 0 0 1 6.5 6.5 6.5 6.5 0 0 1-6.5 6.5 6.5 6.5 0 0 1-6.5-6.5 6.5 6.5 0 0 1 6.5-6.5z"
87+
fill="#1a1a1a"
88+
fill-rule="evenodd"
89+
/>
90+
91+
<!-- Rotator -->
92+
<g fill="#ccc" fill-rule="evenodd">
93+
<rect x="9.05" y="17.4" width="6.95" height="2.47" rx=".756" />
94+
<rect x="9.15" y="26.5" width="6.95" height="2.47" rx=".756" />
95+
<rect x="9.05" y="36.1" width="6.95" height="2.47" rx=".756" />
96+
</g>
97+
98+
<g tabindex="0" @keydown=${this.keydown} @keyup=${this.keyup}>
99+
<rect
100+
x="12.2"
101+
y="8.05"
102+
width="48.4"
103+
height="41"
104+
rx="7.12"
105+
fill="#e6e6e6"
106+
fill-rule="evenodd"
107+
/>
108+
<g>
109+
<g fill-rule="evenodd">
110+
<circle cx="36.6" cy="28.5" r="13.5" fill="#666" />
111+
<rect x="32.5" y="7.87" width="7.42" height="41.5" fill="#666" />
112+
113+
<!-- handle -->
114+
<path
115+
transform="rotate(${this.angle}, 36.244, 28.5)"
116+
d="m36.3 21.4a7.03 7.14 0 0
117+
0-3.74 1.1v12.1a7.03 7.14 0 0 0 3.74 1.1 7.03 7.14 0 0 0 7.03-7.14 7.03 7.14 0 0
118+
0-7.03-7.14z"
119+
fill="#ccc"
120+
stroke="#060606"
121+
stroke-width=".3"
122+
class="handle ${this.pressed ? 'active' : ''}"
123+
@mousedown=${this.press}
124+
@mouseup=${this.release}
125+
/>
126+
</g>
127+
128+
<!-- Counter Clockwise Arrow -->
129+
<path
130+
d="m21 44.5c-5.17-1.78-7.55-5.53-6.6-11.2 0.0662-0.327 0.107-0.938 0.272-1.06 0.204-0.137 0.312-0.116 0.39-0.1 0.0775 0.0152 0.139 0.0274 0.189 0.102 0.846 3.81 3.13 6.84 6.57 7.59 0.304-0.787 0.461-3.32 0.826-3.24 0.428 0.0848 4.31 5.73 4.93 6.65-0.978 0.839-6.07 4.44-6.95 4.28 0 0 0.206-2.19 0.362-2.96z"
131+
fill="#b3b3b3"
132+
stroke="#000"
133+
stroke-width=".0625px"
134+
class="arrow"
135+
@click=${this.counterClockwiseClick}
136+
/>
137+
138+
<!-- Clockwise Arrow -->
139+
<path
140+
d="m21.2 12.1c-5.17 1.78-7.55 5.53-6.6 11.2 0.0662 0.327 0.107 0.938 0.272 1.06 0.204 0.137 0.312 0.116 0.39 0.1 0.0775-0.0152 0.139-0.0274 0.189-0.102 0.846-3.81 3.13-6.84 6.57-7.59 0.304 0.787 0.461 3.32 0.826 3.24 0.428-0.0848 4.31-5.73 4.93-6.65-0.978-0.839-6.07-4.44-6.95-4.28 0 0 0.206 2.19 0.362 2.96z"
141+
fill="#b3b3b3"
142+
stroke="#022"
143+
stroke-width=".0625px"
144+
class="arrow"
145+
@click=${this.clockwiseClick}
146+
/>
147+
</g>
148+
</g>
149+
150+
<!-- Chip Pins -->
151+
<rect
152+
x="83"
153+
y="1.72"
154+
width="10.9"
155+
height="49.2"
156+
fill="url(#a)"
157+
fill-rule="evenodd"
158+
opacity=".65"
159+
stroke="#fff"
160+
stroke-width="1.16"
161+
/>
162+
<g fill="#ccc" fill-rule="evenodd">
163+
<rect x="86.9" y="6.54" width="28.9" height="2.47" rx=".877" />
164+
<rect x="86.8" y="15.9" width="28.9" height="2.47" rx=".877" />
165+
<rect x="87.1" y="25.6" width="28.9" height="2.47" rx=".877" />
166+
<rect x="87.1" y="34.9" width="28.9" height="2.47" rx=".877" />
167+
<rect x="87.6" y="44.1" width="28.9" height="2.47" rx=".877" />
168+
</g>
169+
<g fill="#ffffff" font-family="sans-serif" letter-spacing="0px" word-spacing="0px">
170+
<text x="65.55" y="12.13" font-size="7.29px" fill="#ffffff" stroke-width=".182">CLK</text>
171+
<text x="65.02" y="21.93" font-size="7.44px" fill="#ffffff">DT</text>
172+
<text x="65.29" y="31.26" font-size="7.54px" fill="#ffffff">SW</text>
173+
<text x="70.42" y="39.99" font-size="6.82px" fill="#ffffff">+</text>
174+
<text x="64.31" y="49.74" font-size="7.59px" fill="#ffffff">GND</text>
175+
</g>
176+
</svg>
177+
`;
178+
}
179+
180+
private clockwiseClick() {
181+
this.angle = (this.angle + this.stepSize) % 360;
182+
this.dispatchEvent(new InputEvent('rotate-cw'));
183+
}
184+
185+
private counterClockwiseClick() {
186+
this.angle = (this.angle - this.stepSize + 360) % 360;
187+
this.dispatchEvent(new InputEvent('rotate-ccw'));
188+
}
189+
190+
private press() {
191+
if (!this.pressed) {
192+
this.dispatchEvent(new InputEvent('button-press'));
193+
this.pressed = true;
194+
}
195+
}
196+
197+
private release() {
198+
this.dispatchEvent(new InputEvent('button-release'));
199+
this.pressed = false;
200+
}
201+
202+
private keydown(e: KeyboardEvent) {
203+
switch (e.key) {
204+
case 'ArrowUp':
205+
case 'ArrowRight':
206+
this.clockwiseClick();
207+
e.preventDefault();
208+
break;
209+
210+
case 'ArrowDown':
211+
case 'ArrowLeft':
212+
this.counterClockwiseClick();
213+
e.preventDefault();
214+
break;
215+
216+
case ' ':
217+
this.press();
218+
e.preventDefault();
219+
break;
220+
}
221+
}
222+
223+
private keyup(e: KeyboardEvent) {
224+
switch (e.key) {
225+
case ' ':
226+
e.preventDefault();
227+
this.release();
228+
break;
229+
}
230+
}
231+
}

src/react-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { SmallSoundSensorElement } from './small-sound-sensor-element';
3939
import { BigSoundSensorElement } from './big-sound-sensor-element';
4040
import { MPU6050Element } from './mpu6050-element';
4141
import { ESP32DevkitV1Element } from './esp32-devkit-v1-element';
42+
import { KY040Element } from './ky-040-element';
4243

4344
type WokwiElement<T> = Partial<T> & React.ClassAttributes<T>;
4445

@@ -83,6 +84,7 @@ declare global {
8384
'wokwi-big-sound-sensor': WokwiElement<BigSoundSensorElement>;
8485
'wokwi-mpu6050': WokwiElement<MPU6050Element>;
8586
'wokwi-esp32-devkit-v1': WokwiElement<ESP32DevkitV1Element>;
87+
'wokwi-ky-040': WokwiElement<KY040Element>;
8688
}
8789
}
8890
}

0 commit comments

Comments
 (0)