Rename hst-frontend-new to deck-frontend
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
47
deck-frontend/src/components/Card/Card.ts
Normal file
47
deck-frontend/src/components/Card/Card.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Component } from '../Component.ts';
|
||||
import type { Tag, LangType } from '@/types/index.ts';
|
||||
import { getName, getImg } from '@/utils/index.ts';
|
||||
|
||||
export interface CardProps {
|
||||
tag: Tag;
|
||||
lang: LangType;
|
||||
selected: boolean;
|
||||
selectionMode: boolean;
|
||||
onClick: (mrf: string) => void;
|
||||
onSelect: (mrf: string) => void;
|
||||
}
|
||||
|
||||
export class Card extends Component<CardProps> {
|
||||
protected template(): string {
|
||||
const { tag, lang, selected, selectionMode } = this.props;
|
||||
const img = getImg(tag);
|
||||
const name = getName(tag, lang);
|
||||
|
||||
return `
|
||||
<div class="card ${selected ? 'selected' : ''}" data-mrf="${tag.mrf}">
|
||||
${selectionMode ? `
|
||||
<input type="checkbox" class="card-checkbox" ${selected ? 'checked' : ''}>
|
||||
` : ''}
|
||||
${img
|
||||
? `<img class="card-img" src="${img}" alt="${tag.ref}" loading="lazy">`
|
||||
: `<div class="card-placeholder">${tag.ref?.slice(0, 2) || 'T'}</div>`
|
||||
}
|
||||
<div class="card-name">${name}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected bindEvents(): void {
|
||||
const { onClick, onSelect, selectionMode } = this.props;
|
||||
const mrf = this.props.tag.mrf;
|
||||
|
||||
this.element.addEventListener('click', (e) => {
|
||||
if (selectionMode) {
|
||||
e.preventDefault();
|
||||
onSelect(mrf);
|
||||
} else {
|
||||
onClick(mrf);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
42
deck-frontend/src/components/Component.ts
Normal file
42
deck-frontend/src/components/Component.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export abstract class Component<P extends object = object> {
|
||||
protected element: HTMLElement;
|
||||
protected props: P;
|
||||
|
||||
constructor(props: P) {
|
||||
this.props = props;
|
||||
this.element = this.createElement();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
protected abstract template(): string;
|
||||
|
||||
protected createElement(): HTMLElement {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.innerHTML = this.template().trim();
|
||||
return wrapper.firstElementChild as HTMLElement;
|
||||
}
|
||||
|
||||
protected bindEvents(): void {
|
||||
// Override in subclasses
|
||||
}
|
||||
|
||||
public mount(container: HTMLElement): void {
|
||||
container.appendChild(this.element);
|
||||
}
|
||||
|
||||
public unmount(): void {
|
||||
this.element.remove();
|
||||
}
|
||||
|
||||
public update(props: Partial<P>): void {
|
||||
this.props = { ...this.props, ...props };
|
||||
const newElement = this.createElement();
|
||||
this.element.replaceWith(newElement);
|
||||
this.element = newElement;
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
public getElement(): HTMLElement {
|
||||
return this.element;
|
||||
}
|
||||
}
|
||||
46
deck-frontend/src/components/Modal/Modal.ts
Normal file
46
deck-frontend/src/components/Modal/Modal.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Component } from '../Component.ts';
|
||||
|
||||
export interface ModalProps {
|
||||
title: string;
|
||||
content: string;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export class Modal extends Component<ModalProps> {
|
||||
protected template(): string {
|
||||
const { title, content, isOpen } = this.props;
|
||||
return `
|
||||
<div class="modal ${isOpen ? 'open' : ''}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>${title}</h3>
|
||||
<button class="modal-close">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
${content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected bindEvents(): void {
|
||||
const closeBtn = this.element.querySelector('.modal-close');
|
||||
closeBtn?.addEventListener('click', this.props.onClose);
|
||||
|
||||
this.element.addEventListener('click', (e) => {
|
||||
if (e.target === this.element) {
|
||||
this.props.onClose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public open(): void {
|
||||
this.element.classList.add('open');
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.element.classList.remove('open');
|
||||
}
|
||||
}
|
||||
25
deck-frontend/src/components/TagChip/TagChip.ts
Normal file
25
deck-frontend/src/components/TagChip/TagChip.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Component } from '../Component.ts';
|
||||
|
||||
export interface TagChipProps {
|
||||
mrf: string;
|
||||
label: string;
|
||||
title?: string;
|
||||
onClick: (mrf: string) => void;
|
||||
}
|
||||
|
||||
export class TagChip extends Component<TagChipProps> {
|
||||
protected template(): string {
|
||||
const { mrf, label, title } = this.props;
|
||||
return `
|
||||
<span class="tag-chip" data-mrf="${mrf}" title="${title || ''}">
|
||||
${label}
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
protected bindEvents(): void {
|
||||
this.element.addEventListener('click', () => {
|
||||
this.props.onClick(this.props.mrf);
|
||||
});
|
||||
}
|
||||
}
|
||||
4
deck-frontend/src/components/index.ts
Normal file
4
deck-frontend/src/components/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { Component } from './Component.ts';
|
||||
export { Card, type CardProps } from './Card/Card.ts';
|
||||
export { TagChip, type TagChipProps } from './TagChip/TagChip.ts';
|
||||
export { Modal, type ModalProps } from './Modal/Modal.ts';
|
||||
Reference in New Issue
Block a user