711 lines
19 KiB
TypeScript
711 lines
19 KiB
TypeScript
/**
|
|
* folk-gov-amendment — Circuit Modification Proposal
|
|
*
|
|
* References a target shape to modify, contains proposed replacement data.
|
|
* Built-in approval mechanism (majority/unanimous/single) with inline voter list.
|
|
* Shows a "Before → After" diff view. On approval, dispatches gov-amendment-apply
|
|
* event to replace the target shape while preserving its ID.
|
|
*/
|
|
|
|
import { FolkShape } from "./folk-shape";
|
|
import { css, html } from "./tags";
|
|
import type { PortDescriptor } from "./data-types";
|
|
|
|
const HEADER_COLOR = "#be185d";
|
|
|
|
type ApprovalMode = "single" | "majority" | "unanimous";
|
|
|
|
interface Vote {
|
|
voter: string;
|
|
approve: boolean;
|
|
timestamp: number;
|
|
}
|
|
|
|
const styles = css`
|
|
:host {
|
|
background: var(--rs-bg-surface, #1e293b);
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25);
|
|
min-width: 280px;
|
|
min-height: 200px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 8px 12px;
|
|
background: ${HEADER_COLOR};
|
|
color: white;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
cursor: move;
|
|
border-radius: 10px 10px 0 0;
|
|
}
|
|
|
|
.header-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.header-actions button {
|
|
background: transparent;
|
|
border: none;
|
|
color: white;
|
|
cursor: pointer;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.header-actions button:hover {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
.body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 12px;
|
|
gap: 8px;
|
|
overflow-y: auto;
|
|
max-height: calc(100% - 36px);
|
|
}
|
|
|
|
.title-input {
|
|
background: transparent;
|
|
border: none;
|
|
color: var(--rs-text-primary, #e2e8f0);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
width: 100%;
|
|
outline: none;
|
|
}
|
|
|
|
.title-input::placeholder {
|
|
color: var(--rs-text-muted, #64748b);
|
|
}
|
|
|
|
.field-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 11px;
|
|
color: var(--rs-text-muted, #94a3b8);
|
|
}
|
|
|
|
.field-input {
|
|
background: rgba(255, 255, 255, 0.06);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
border-radius: 4px;
|
|
color: var(--rs-text-primary, #e2e8f0);
|
|
font-size: 11px;
|
|
padding: 3px 6px;
|
|
outline: none;
|
|
flex: 1;
|
|
}
|
|
|
|
.mode-select {
|
|
background: rgba(255, 255, 255, 0.06);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
border-radius: 4px;
|
|
color: var(--rs-text-primary, #e2e8f0);
|
|
font-size: 11px;
|
|
padding: 3px 6px;
|
|
outline: none;
|
|
}
|
|
|
|
.diff-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.diff-label {
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
color: var(--rs-text-muted, #64748b);
|
|
}
|
|
|
|
.diff-box {
|
|
background: rgba(255, 255, 255, 0.04);
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
border-radius: 4px;
|
|
padding: 6px 8px;
|
|
font-size: 10px;
|
|
font-family: monospace;
|
|
color: var(--rs-text-secondary, #94a3b8);
|
|
max-height: 60px;
|
|
overflow-y: auto;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.diff-box.before {
|
|
border-left: 3px solid #ef4444;
|
|
}
|
|
|
|
.diff-box.after {
|
|
border-left: 3px solid #22c55e;
|
|
}
|
|
|
|
.diff-arrow {
|
|
text-align: center;
|
|
font-size: 14px;
|
|
color: var(--rs-text-muted, #475569);
|
|
}
|
|
|
|
.desc-textarea {
|
|
background: rgba(255, 255, 255, 0.04);
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
border-radius: 4px;
|
|
color: var(--rs-text-secondary, #cbd5e1);
|
|
font-size: 11px;
|
|
padding: 6px 8px;
|
|
outline: none;
|
|
resize: none;
|
|
min-height: 36px;
|
|
}
|
|
|
|
.desc-textarea::placeholder {
|
|
color: var(--rs-text-muted, #475569);
|
|
}
|
|
|
|
.voters-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.voter-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
font-size: 11px;
|
|
padding: 3px 6px;
|
|
border-radius: 4px;
|
|
background: rgba(255, 255, 255, 0.03);
|
|
}
|
|
|
|
.voter-name {
|
|
color: var(--rs-text-primary, #e2e8f0);
|
|
}
|
|
|
|
.vote-badge {
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
padding: 1px 6px;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.vote-badge.approve {
|
|
background: rgba(34, 197, 94, 0.2);
|
|
color: #22c55e;
|
|
}
|
|
|
|
.vote-badge.reject {
|
|
background: rgba(239, 68, 68, 0.2);
|
|
color: #ef4444;
|
|
}
|
|
|
|
.vote-actions {
|
|
display: flex;
|
|
gap: 4px;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.vote-name-input {
|
|
background: rgba(255, 255, 255, 0.06);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
border-radius: 4px;
|
|
color: var(--rs-text-primary, #e2e8f0);
|
|
font-size: 11px;
|
|
padding: 3px 6px;
|
|
outline: none;
|
|
flex: 1;
|
|
}
|
|
|
|
.vote-btn {
|
|
border: none;
|
|
border-radius: 4px;
|
|
padding: 3px 8px;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.vote-btn.approve {
|
|
background: #22c55e;
|
|
color: white;
|
|
}
|
|
|
|
.vote-btn.reject {
|
|
background: #ef4444;
|
|
color: white;
|
|
}
|
|
|
|
.vote-btn:hover {
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.apply-btn {
|
|
background: ${HEADER_COLOR};
|
|
border: none;
|
|
color: white;
|
|
border-radius: 6px;
|
|
padding: 6px 12px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
text-align: center;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.apply-btn:hover {
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.apply-btn:disabled {
|
|
opacity: 0.4;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.status-badge {
|
|
display: inline-block;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
padding: 2px 8px;
|
|
border-radius: 4px;
|
|
text-align: center;
|
|
}
|
|
|
|
.status-badge.pending {
|
|
background: rgba(245, 158, 11, 0.2);
|
|
color: #f59e0b;
|
|
}
|
|
|
|
.status-badge.approved {
|
|
background: rgba(34, 197, 94, 0.2);
|
|
color: #22c55e;
|
|
}
|
|
|
|
.status-badge.rejected {
|
|
background: rgba(239, 68, 68, 0.2);
|
|
color: #ef4444;
|
|
}
|
|
|
|
.status-badge.applied {
|
|
background: rgba(29, 78, 216, 0.2);
|
|
color: #60a5fa;
|
|
}
|
|
`;
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"folk-gov-amendment": FolkGovAmendment;
|
|
}
|
|
}
|
|
|
|
export class FolkGovAmendment extends FolkShape {
|
|
static override tagName = "folk-gov-amendment";
|
|
|
|
static override portDescriptors: PortDescriptor[] = [
|
|
{ name: "approval-in", type: "json", direction: "input" },
|
|
{ name: "amendment-out", type: "json", direction: "output" },
|
|
{ name: "gate-out", type: "json", direction: "output" },
|
|
];
|
|
|
|
static {
|
|
const sheet = new CSSStyleSheet();
|
|
const parentRules = Array.from(FolkShape.styles.cssRules).map(r => r.cssText).join("\n");
|
|
const childRules = Array.from(styles.cssRules).map(r => r.cssText).join("\n");
|
|
sheet.replaceSync(`${parentRules}\n${childRules}`);
|
|
this.styles = sheet;
|
|
}
|
|
|
|
#title = "Amendment";
|
|
#targetShapeId = "";
|
|
#replacementType = "folk-gov-binary"; // what to replace with
|
|
#replacementData: Record<string, any> = {};
|
|
#approvalMode: ApprovalMode = "single";
|
|
#votes: Vote[] = [];
|
|
#amendmentStatus: "pending" | "approved" | "rejected" | "applied" = "pending";
|
|
#description = "";
|
|
|
|
// DOM refs
|
|
#titleEl!: HTMLInputElement;
|
|
#targetEl!: HTMLInputElement;
|
|
#replTypeEl!: HTMLInputElement;
|
|
#modeEl!: HTMLSelectElement;
|
|
#descEl!: HTMLTextAreaElement;
|
|
#votersEl!: HTMLElement;
|
|
#statusEl!: HTMLElement;
|
|
#applyBtn!: HTMLButtonElement;
|
|
#beforeBox!: HTMLElement;
|
|
#voteNameEl!: HTMLInputElement;
|
|
|
|
get title() { return this.#title; }
|
|
set title(v: string) {
|
|
this.#title = v;
|
|
if (this.#titleEl) this.#titleEl.value = v;
|
|
}
|
|
|
|
get targetShapeId() { return this.#targetShapeId; }
|
|
set targetShapeId(v: string) {
|
|
this.#targetShapeId = v;
|
|
if (this.#targetEl) this.#targetEl.value = v;
|
|
this.#updateDiff();
|
|
}
|
|
|
|
get replacementType() { return this.#replacementType; }
|
|
set replacementType(v: string) {
|
|
this.#replacementType = v;
|
|
if (this.#replTypeEl) this.#replTypeEl.value = v;
|
|
}
|
|
|
|
get replacementData() { return this.#replacementData; }
|
|
set replacementData(v: Record<string, any>) { this.#replacementData = v; }
|
|
|
|
get approvalMode(): ApprovalMode { return this.#approvalMode; }
|
|
set approvalMode(v: ApprovalMode) {
|
|
this.#approvalMode = v;
|
|
if (this.#modeEl) this.#modeEl.value = v;
|
|
}
|
|
|
|
get votes() { return [...this.#votes]; }
|
|
set votes(v: Vote[]) {
|
|
this.#votes = v;
|
|
this.#renderVoters();
|
|
this.#checkApproval();
|
|
}
|
|
|
|
get amendmentStatus() { return this.#amendmentStatus; }
|
|
set amendmentStatus(v: "pending" | "approved" | "rejected" | "applied") {
|
|
this.#amendmentStatus = v;
|
|
this.#updateStatusBadge();
|
|
}
|
|
|
|
get description() { return this.#description; }
|
|
set description(v: string) {
|
|
this.#description = v;
|
|
if (this.#descEl) this.#descEl.value = v;
|
|
}
|
|
|
|
override createRenderRoot() {
|
|
const root = super.createRenderRoot();
|
|
this.initPorts();
|
|
|
|
const wrapper = document.createElement("div");
|
|
wrapper.style.cssText = "width:100%;height:100%;display:flex;flex-direction:column;";
|
|
wrapper.innerHTML = html`
|
|
<div class="header" data-drag>
|
|
<span class="header-title">📝 Amendment</span>
|
|
<span class="header-actions">
|
|
<button class="close-btn">×</button>
|
|
</span>
|
|
</div>
|
|
<div class="body">
|
|
<input class="title-input" type="text" placeholder="Amendment title..." />
|
|
<div class="field-row">
|
|
<span>Target:</span>
|
|
<input class="field-input target-id" type="text" placeholder="shape ID to replace" />
|
|
</div>
|
|
<div class="field-row">
|
|
<span>Replace with:</span>
|
|
<input class="field-input repl-type" type="text" placeholder="folk-gov-binary" />
|
|
</div>
|
|
<div class="field-row">
|
|
<span>Approval:</span>
|
|
<select class="mode-select">
|
|
<option value="single">Single</option>
|
|
<option value="majority">Majority</option>
|
|
<option value="unanimous">Unanimous</option>
|
|
</select>
|
|
</div>
|
|
<textarea class="desc-textarea" placeholder="Describe what this amendment changes..." rows="2"></textarea>
|
|
<div class="diff-section">
|
|
<span class="diff-label">Before</span>
|
|
<div class="diff-box before"></div>
|
|
<div class="diff-arrow">↓</div>
|
|
<span class="diff-label">After</span>
|
|
<div class="diff-box after"></div>
|
|
</div>
|
|
<div class="voters-section"></div>
|
|
<div class="vote-actions">
|
|
<input class="vote-name-input" type="text" placeholder="Your name" />
|
|
<button class="vote-btn approve">✓</button>
|
|
<button class="vote-btn reject">✗</button>
|
|
</div>
|
|
<span class="status-badge pending">PENDING</span>
|
|
<button class="apply-btn" disabled>Apply Amendment</button>
|
|
</div>
|
|
`;
|
|
|
|
const slot = root.querySelector("slot");
|
|
const container = slot?.parentElement as HTMLElement;
|
|
if (container) container.replaceWith(wrapper);
|
|
|
|
// Cache refs
|
|
this.#titleEl = wrapper.querySelector(".title-input") as HTMLInputElement;
|
|
this.#targetEl = wrapper.querySelector(".target-id") as HTMLInputElement;
|
|
this.#replTypeEl = wrapper.querySelector(".repl-type") as HTMLInputElement;
|
|
this.#modeEl = wrapper.querySelector(".mode-select") as HTMLSelectElement;
|
|
this.#descEl = wrapper.querySelector(".desc-textarea") as HTMLTextAreaElement;
|
|
this.#votersEl = wrapper.querySelector(".voters-section") as HTMLElement;
|
|
this.#statusEl = wrapper.querySelector(".status-badge") as HTMLElement;
|
|
this.#applyBtn = wrapper.querySelector(".apply-btn") as HTMLButtonElement;
|
|
this.#beforeBox = wrapper.querySelector(".diff-box.before") as HTMLElement;
|
|
this.#voteNameEl = wrapper.querySelector(".vote-name-input") as HTMLInputElement;
|
|
const afterBox = wrapper.querySelector(".diff-box.after") as HTMLElement;
|
|
|
|
// Set initial values
|
|
this.#titleEl.value = this.#title;
|
|
this.#targetEl.value = this.#targetShapeId;
|
|
this.#replTypeEl.value = this.#replacementType;
|
|
this.#modeEl.value = this.#approvalMode;
|
|
this.#descEl.value = this.#description;
|
|
afterBox.textContent = this.#replacementType;
|
|
this.#updateDiff();
|
|
this.#renderVoters();
|
|
this.#updateStatusBadge();
|
|
|
|
// Wire events
|
|
const onChange = () => this.dispatchEvent(new CustomEvent("content-change"));
|
|
|
|
this.#titleEl.addEventListener("input", (e) => {
|
|
e.stopPropagation();
|
|
this.#title = this.#titleEl.value;
|
|
onChange();
|
|
});
|
|
|
|
this.#targetEl.addEventListener("input", (e) => {
|
|
e.stopPropagation();
|
|
this.#targetShapeId = this.#targetEl.value;
|
|
this.#updateDiff();
|
|
onChange();
|
|
});
|
|
|
|
this.#replTypeEl.addEventListener("input", (e) => {
|
|
e.stopPropagation();
|
|
this.#replacementType = this.#replTypeEl.value;
|
|
afterBox.textContent = `type: ${this.#replacementType}`;
|
|
onChange();
|
|
});
|
|
|
|
this.#modeEl.addEventListener("change", (e) => {
|
|
e.stopPropagation();
|
|
this.#approvalMode = this.#modeEl.value as ApprovalMode;
|
|
this.#checkApproval();
|
|
onChange();
|
|
});
|
|
|
|
this.#descEl.addEventListener("input", (e) => {
|
|
e.stopPropagation();
|
|
this.#description = this.#descEl.value;
|
|
onChange();
|
|
});
|
|
|
|
// Vote buttons
|
|
wrapper.querySelector(".vote-btn.approve")!.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
this.#castVote(true);
|
|
onChange();
|
|
});
|
|
|
|
wrapper.querySelector(".vote-btn.reject")!.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
this.#castVote(false);
|
|
onChange();
|
|
});
|
|
|
|
// Apply button
|
|
this.#applyBtn.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
this.#applyAmendment();
|
|
onChange();
|
|
});
|
|
|
|
wrapper.querySelector(".close-btn")!.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
this.dispatchEvent(new CustomEvent("close"));
|
|
});
|
|
|
|
// Prevent drag on inputs
|
|
for (const el of wrapper.querySelectorAll("input, textarea, select, button")) {
|
|
el.addEventListener("pointerdown", (e) => e.stopPropagation());
|
|
}
|
|
|
|
// Handle approval-in port
|
|
this.addEventListener("port-value-changed", ((e: CustomEvent) => {
|
|
const { name, value } = e.detail;
|
|
if (name === "approval-in" && value && typeof value === "object") {
|
|
const v = value as any;
|
|
if (v.voter && v.approve !== undefined) {
|
|
this.#votes.push({
|
|
voter: v.voter,
|
|
approve: !!v.approve,
|
|
timestamp: v.timestamp || Date.now(),
|
|
});
|
|
this.#renderVoters();
|
|
this.#checkApproval();
|
|
onChange();
|
|
}
|
|
}
|
|
}) as EventListener);
|
|
|
|
return root;
|
|
}
|
|
|
|
#updateDiff() {
|
|
if (!this.#beforeBox) return;
|
|
if (!this.#targetShapeId) {
|
|
this.#beforeBox.textContent = "(no target selected)";
|
|
return;
|
|
}
|
|
const target = document.getElementById(this.#targetShapeId) as any;
|
|
if (!target) {
|
|
this.#beforeBox.textContent = `(shape "${this.#targetShapeId}" not found)`;
|
|
return;
|
|
}
|
|
const data = target.toJSON?.() || {};
|
|
this.#beforeBox.textContent = `type: ${data.type || target.tagName}\ntitle: ${data.title || "—"}`;
|
|
}
|
|
|
|
#castVote(approve: boolean) {
|
|
const voter = this.#voteNameEl?.value.trim() || "anonymous";
|
|
// Prevent duplicate votes from same voter
|
|
const existing = this.#votes.findIndex(v => v.voter === voter);
|
|
if (existing >= 0) {
|
|
this.#votes[existing] = { voter, approve, timestamp: Date.now() };
|
|
} else {
|
|
this.#votes.push({ voter, approve, timestamp: Date.now() });
|
|
}
|
|
if (this.#voteNameEl) this.#voteNameEl.value = "";
|
|
this.#renderVoters();
|
|
this.#checkApproval();
|
|
}
|
|
|
|
#renderVoters() {
|
|
if (!this.#votersEl) return;
|
|
this.#votersEl.innerHTML = this.#votes.map(v => {
|
|
const badge = v.approve
|
|
? `<span class="vote-badge approve">YES</span>`
|
|
: `<span class="vote-badge reject">NO</span>`;
|
|
return `<div class="voter-row"><span class="voter-name">${v.voter}</span>${badge}</div>`;
|
|
}).join("");
|
|
}
|
|
|
|
#checkApproval() {
|
|
if (this.#amendmentStatus === "applied") return;
|
|
|
|
const approvals = this.#votes.filter(v => v.approve).length;
|
|
const rejections = this.#votes.filter(v => !v.approve).length;
|
|
const total = this.#votes.length;
|
|
|
|
let approved = false;
|
|
if (this.#approvalMode === "single") {
|
|
approved = approvals >= 1;
|
|
} else if (this.#approvalMode === "majority") {
|
|
approved = total > 0 && approvals > total / 2;
|
|
} else if (this.#approvalMode === "unanimous") {
|
|
approved = total > 0 && rejections === 0;
|
|
}
|
|
|
|
if (approved) {
|
|
this.#amendmentStatus = "approved";
|
|
} else if (total > 0 && this.#approvalMode === "unanimous" && rejections > 0) {
|
|
this.#amendmentStatus = "rejected";
|
|
} else {
|
|
this.#amendmentStatus = "pending";
|
|
}
|
|
|
|
this.#updateStatusBadge();
|
|
this.#emitPorts();
|
|
}
|
|
|
|
#updateStatusBadge() {
|
|
if (!this.#statusEl || !this.#applyBtn) return;
|
|
this.#statusEl.textContent = this.#amendmentStatus.toUpperCase();
|
|
this.#statusEl.className = `status-badge ${this.#amendmentStatus}`;
|
|
this.#applyBtn.disabled = this.#amendmentStatus !== "approved";
|
|
}
|
|
|
|
#applyAmendment() {
|
|
if (this.#amendmentStatus !== "approved") return;
|
|
if (!this.#targetShapeId) return;
|
|
|
|
// Dispatch event for canvas to handle the shape replacement
|
|
this.dispatchEvent(new CustomEvent("gov-amendment-apply", {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: {
|
|
targetShapeId: this.#targetShapeId,
|
|
replacementType: this.#replacementType,
|
|
replacementData: this.#replacementData,
|
|
amendmentId: this.id,
|
|
},
|
|
}));
|
|
|
|
this.#amendmentStatus = "applied";
|
|
this.#updateStatusBadge();
|
|
this.#emitPorts();
|
|
}
|
|
|
|
#emitPorts() {
|
|
const data = {
|
|
status: this.#amendmentStatus,
|
|
targetShapeId: this.#targetShapeId,
|
|
approved: this.#amendmentStatus === "approved" || this.#amendmentStatus === "applied",
|
|
};
|
|
this.setPortValue("amendment-out", data);
|
|
this.setPortValue("gate-out", {
|
|
satisfied: this.#amendmentStatus === "approved" || this.#amendmentStatus === "applied",
|
|
});
|
|
}
|
|
|
|
override toJSON() {
|
|
return {
|
|
...super.toJSON(),
|
|
type: "folk-gov-amendment",
|
|
title: this.#title,
|
|
targetShapeId: this.#targetShapeId,
|
|
replacementType: this.#replacementType,
|
|
replacementData: this.#replacementData,
|
|
approvalMode: this.#approvalMode,
|
|
votes: this.#votes,
|
|
amendmentStatus: this.#amendmentStatus,
|
|
description: this.#description,
|
|
};
|
|
}
|
|
|
|
static override fromData(data: Record<string, any>): FolkGovAmendment {
|
|
const shape = FolkShape.fromData.call(this, data) as FolkGovAmendment;
|
|
if (data.title !== undefined) shape.title = data.title;
|
|
if (data.targetShapeId !== undefined) shape.targetShapeId = data.targetShapeId;
|
|
if (data.replacementType !== undefined) shape.replacementType = data.replacementType;
|
|
if (data.replacementData !== undefined) shape.replacementData = data.replacementData;
|
|
if (data.approvalMode !== undefined) shape.approvalMode = data.approvalMode;
|
|
if (data.votes !== undefined) shape.votes = data.votes;
|
|
if (data.amendmentStatus !== undefined) shape.amendmentStatus = data.amendmentStatus;
|
|
if (data.description !== undefined) shape.description = data.description;
|
|
return shape;
|
|
}
|
|
|
|
override applyData(data: Record<string, any>): void {
|
|
super.applyData(data);
|
|
if (data.title !== undefined && data.title !== this.#title) this.title = data.title;
|
|
if (data.targetShapeId !== undefined && data.targetShapeId !== this.#targetShapeId) this.targetShapeId = data.targetShapeId;
|
|
if (data.replacementType !== undefined && data.replacementType !== this.#replacementType) this.replacementType = data.replacementType;
|
|
if (data.replacementData !== undefined) this.replacementData = data.replacementData;
|
|
if (data.approvalMode !== undefined && data.approvalMode !== this.#approvalMode) this.approvalMode = data.approvalMode;
|
|
if (data.votes !== undefined) this.votes = data.votes;
|
|
if (data.amendmentStatus !== undefined && data.amendmentStatus !== this.#amendmentStatus) this.amendmentStatus = data.amendmentStatus;
|
|
if (data.description !== undefined && data.description !== this.#description) this.description = data.description;
|
|
}
|
|
}
|