Skip to content

Commit 3e64d36

Browse files
Refactor icon handling with image-icon component and store
Introduced a new <image-icon> component and a shared image store to centralize SVG icon sources for add, delete, and edit actions. Updated django-hstore-widget to use the new component and store, simplifying icon rendering and improving maintainability. Added @stencil/store as a dev dependency and updated tsconfig paths for the new store.
1 parent 3491f2f commit 3e64d36

File tree

7 files changed

+82
-13
lines changed

7 files changed

+82
-13
lines changed

package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"devDependencies": {
2323
"@stencil/core": "^4.36.2",
2424
"@stencil/sass": "^3.2.2",
25+
"@stencil/store": "^2.2.0",
2526
"@types/jest": "^29.5.14",
2627
"@types/node": "^24.2.1",
2728
"jest": "^29.7.0",

src/frontend/components.d.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ export namespace Components {
3030
*/
3131
"rows": number;
3232
}
33+
interface ImageIcon {
34+
"type": 'delete' | 'add' | 'edit';
35+
}
3336
}
3437
declare global {
3538
interface HTMLDjangoHstoreWidgetElement extends Components.DjangoHstoreWidget, HTMLStencilElement {
@@ -38,8 +41,15 @@ declare global {
3841
prototype: HTMLDjangoHstoreWidgetElement;
3942
new (): HTMLDjangoHstoreWidgetElement;
4043
};
44+
interface HTMLImageIconElement extends Components.ImageIcon, HTMLStencilElement {
45+
}
46+
var HTMLImageIconElement: {
47+
prototype: HTMLImageIconElement;
48+
new (): HTMLImageIconElement;
49+
};
4150
interface HTMLElementTagNameMap {
4251
"django-hstore-widget": HTMLDjangoHstoreWidgetElement;
52+
"image-icon": HTMLImageIconElement;
4353
}
4454
}
4555
declare namespace LocalJSX {
@@ -67,15 +77,20 @@ declare namespace LocalJSX {
6777
*/
6878
"rows"?: number;
6979
}
80+
interface ImageIcon {
81+
"type": 'delete' | 'add' | 'edit';
82+
}
7083
interface IntrinsicElements {
7184
"django-hstore-widget": DjangoHstoreWidget;
85+
"image-icon": ImageIcon;
7286
}
7387
}
7488
export { LocalJSX as JSX };
7589
declare module "@stencil/core" {
7690
export namespace JSX {
7791
interface IntrinsicElements {
7892
"django-hstore-widget": LocalJSX.DjangoHstoreWidget & JSXBase.HTMLAttributes<HTMLDjangoHstoreWidgetElement>;
93+
"image-icon": LocalJSX.ImageIcon & JSXBase.HTMLAttributes<HTMLImageIconElement>;
7994
}
8095
}
8196
}

src/frontend/components/django-hstore-widget/django-hstore-widget.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Component, Host, h, Prop, State, Fragment, Watch } from '@stencil/core';
22
import { cn } from '$lib/classnames';
3+
import imageState from '$store/image';
34

45
const django_mapping = Object.freeze({
56
input: 'vTextField',
@@ -49,6 +50,18 @@ export class DjangoHstoreWidget {
4950
}
5051

5152
// Callbacks
53+
async componentWillLoad() {
54+
if (this.delete_svg_src) {
55+
imageState.delete_svg_src = this.delete_svg_src;
56+
}
57+
if (this.add_svg_src) {
58+
imageState.add_svg_src = this.add_svg_src;
59+
}
60+
if (this.edit_svg_src) {
61+
imageState.edit_svg_src = this.edit_svg_src;
62+
}
63+
}
64+
5265
async componentDidLoad() {
5366
globalThis.requestAnimationFrame(async () => {
5467
await this.#parseJson(this.json);
@@ -181,21 +194,13 @@ export class DjangoHstoreWidget {
181194
id="delete-button"
182195
onClick={this.#handleDelete.bind(this, item.index)}
183196
>
184-
{this.#ImageIconComponent({ type: 'delete' })}
197+
<image-icon type="delete"></image-icon>
185198
</div>
186199
</div>
187200
</div>
188201
);
189202
}
190203

191-
#ImageIconComponent({ type }: { type: 'delete' | 'add' | 'edit' }) {
192-
const mapping = { delete: { src: this.delete_svg_src, alt: '❌' }, add: { src: this.add_svg_src, alt: '➕' }, edit: { src: this.edit_svg_src, alt: '✏️' } };
193-
194-
const icon = mapping[type];
195-
if (!icon) throw new Error(`Icon type "${type}" is not defined in the mapping.`);
196-
return <img src={icon.src} alt={icon.alt} />;
197-
}
198-
199204
render() {
200205
if (!this.mounted) {
201206
return (
@@ -241,7 +246,7 @@ export class DjangoHstoreWidget {
241246
aria-label="Add Row"
242247
onClick={this.#handleRowAdd.bind(this)}
243248
>
244-
{this.#ImageIconComponent({ type: 'add' })}
249+
<image-icon type="add"></image-icon>
245250
Add row
246251
</button>
247252

@@ -253,7 +258,7 @@ export class DjangoHstoreWidget {
253258
aria-label="Close TextArea"
254259
onClick={this.#handleToggleClick.bind(this)}
255260
>
256-
{this.#ImageIconComponent({ type: 'delete' })}
261+
<image-icon type="delete"></image-icon>
257262
Close TextArea
258263
</button>
259264
) : this.output_render_type === 'rows' ? (
@@ -263,7 +268,7 @@ export class DjangoHstoreWidget {
263268
aria-label="Open TextArea"
264269
onClick={this.#handleToggleClick.bind(this)}
265270
>
266-
{this.#ImageIconComponent({ type: 'edit' })}
271+
<image-icon type="edit"></image-icon>
267272
Open TextArea
268273
</button>
269274
) : (
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Component, h, Host, Prop } from '@stencil/core';
2+
import imageState from '$store/image';
3+
@Component({
4+
tag: 'image-icon',
5+
shadow: true,
6+
})
7+
export class ImageIcon {
8+
@Prop() type!: 'delete' | 'add' | 'edit';
9+
10+
render() {
11+
const mapping = {
12+
delete: { src: imageState.delete_svg_src, alt: '❌' },
13+
add: { src: imageState.add_svg_src, alt: '➕' },
14+
edit: { src: imageState.edit_svg_src, alt: '✏️' },
15+
};
16+
const icon = mapping[this.type];
17+
if (!icon?.src) console.error(`Icon type "${this.type}" src is missing`);
18+
return (
19+
<Host>
20+
<img src={icon.src} alt={icon.alt} />
21+
</Host>
22+
);
23+
}
24+
}

src/frontend/stores/image.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createStore } from '@stencil/store';
2+
3+
const { state } = createStore({
4+
delete_svg_src: '',
5+
add_svg_src: '',
6+
edit_svg_src: '',
7+
});
8+
export default state;

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"jsx": "react",
1717
"jsxFactory": "h",
1818
"paths": {
19-
"$lib/*": ["lib/*"]
19+
"$lib/*": ["lib/*"],
20+
"$store/*": ["stores/*"]
2021
}
2122
},
2223
"include": ["src/frontend"],

0 commit comments

Comments
 (0)