odontogram 0.0.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/index.d.mts +35 -0
  2. package/dist/index.mjs +297 -0
  3. package/package.json +48 -100
  4. package/readme.md +140 -0
  5. package/src/index.ts +2 -0
  6. package/src/og-odontogram.ts +109 -0
  7. package/src/og-tooth.ts +76 -0
  8. package/src/stories/Layout.stories.ts +64 -0
  9. package/src/stories/Odontogram.stories.ts +68 -0
  10. package/LICENSE +0 -21
  11. package/README.md +0 -299
  12. package/cdn/components/button/button.d.ts +0 -25
  13. package/cdn/components/button/button.d.ts.map +0 -1
  14. package/cdn/components/button/button.js +0 -598
  15. package/cdn/components/button/button.styles.d.ts +0 -3
  16. package/cdn/components/button/button.styles.d.ts.map +0 -1
  17. package/cdn/components/button/index.d.ts +0 -2
  18. package/cdn/components/button/index.d.ts.map +0 -1
  19. package/cdn/components/button/index.js +0 -2
  20. package/cdn/components/index.d.ts +0 -2
  21. package/cdn/components/index.d.ts.map +0 -1
  22. package/cdn/components/index.js +0 -1
  23. package/cdn/index.d.ts +0 -2
  24. package/cdn/index.d.ts.map +0 -1
  25. package/cdn/index.js +0 -1
  26. package/cdn/loader.js +0 -118
  27. package/custom-elements.json +0 -152
  28. package/dist/components/button/button.d.ts +0 -25
  29. package/dist/components/button/button.d.ts.map +0 -1
  30. package/dist/components/button/button.js +0 -47
  31. package/dist/components/button/button.js.map +0 -1
  32. package/dist/components/button/button.styles.d.ts +0 -3
  33. package/dist/components/button/button.styles.d.ts.map +0 -1
  34. package/dist/components/button/button.styles.js +0 -43
  35. package/dist/components/button/button.styles.js.map +0 -1
  36. package/dist/components/button/index.d.ts +0 -2
  37. package/dist/components/button/index.d.ts.map +0 -1
  38. package/dist/components/button/index.js +0 -3
  39. package/dist/components/button/index.js.map +0 -1
  40. package/dist/components/index.d.ts +0 -2
  41. package/dist/components/index.d.ts.map +0 -1
  42. package/dist/components/index.js +0 -2
  43. package/dist/components/index.js.map +0 -1
  44. package/dist/index.d.ts +0 -2
  45. package/dist/index.d.ts.map +0 -1
  46. package/dist/index.js +0 -2
  47. package/dist/index.js.map +0 -1
  48. package/react/MyButton.d.ts +0 -90
  49. package/react/MyButton.js +0 -32
  50. package/react/index.d.ts +0 -1
  51. package/react/index.js +0 -1
  52. package/react/react-utils.js +0 -67
  53. package/types/custom-element-jsx.d.ts +0 -236
  54. package/types/custom-element-svelte.d.ts +0 -70
  55. package/types/custom-element-vuejs.d.ts +0 -40
@@ -0,0 +1,35 @@
1
+ import * as lit from "lit";
2
+ import { LitElement } from "lit";
3
+
4
+ //#region src/og-tooth.d.ts
5
+ interface ToothSurfaces {
6
+ vestibular: boolean;
7
+ distal: boolean;
8
+ palatine: boolean;
9
+ mesial: boolean;
10
+ occlusal: boolean;
11
+ }
12
+ declare class OgTooth extends LitElement {
13
+ toothId: string;
14
+ colorClick: string;
15
+ selections: ToothSurfaces;
16
+ private _toggle;
17
+ render(): lit.TemplateResult<1>;
18
+ static styles: lit.CSSResult;
19
+ }
20
+ //#endregion
21
+ //#region src/og-odontogram.d.ts
22
+ type PatientMode = 'adult' | 'baby' | 'old';
23
+ declare class OgDontogram extends LitElement {
24
+ mode: PatientMode;
25
+ chartData: Record<string, ToothSurfaces>;
26
+ private teethState;
27
+ private layouts;
28
+ willUpdate(changedProperties: Map<string, any>): void;
29
+ private _updateState;
30
+ renderTooth(id: number): lit.TemplateResult<1>;
31
+ render(): lit.TemplateResult<1>;
32
+ static styles: lit.CSSResult;
33
+ }
34
+ //#endregion
35
+ export { OgDontogram, OgTooth, PatientMode, ToothSurfaces };
package/dist/index.mjs ADDED
@@ -0,0 +1,297 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { customElement, property, state } from "lit/decorators.js";
3
+
4
+ //#region \0@oxc-project+runtime@0.114.0/helpers/decorate.js
5
+ function __decorate(decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ }
11
+
12
+ //#endregion
13
+ //#region src/og-tooth.ts
14
+ let OgTooth = class OgTooth extends LitElement {
15
+ constructor(..._args) {
16
+ super(..._args);
17
+ this.toothId = "";
18
+ this.colorClick = "#ff6961";
19
+ this.selections = {
20
+ vestibular: false,
21
+ distal: false,
22
+ palatine: false,
23
+ mesial: false,
24
+ occlusal: false
25
+ };
26
+ }
27
+ _toggle(surface, clickFn, unclickFn) {
28
+ const isSelected = !this.selections[surface];
29
+ this.selections = {
30
+ ...this.selections,
31
+ [surface]: isSelected
32
+ };
33
+ const eventName = isSelected ? clickFn : unclickFn;
34
+ this.dispatchEvent(new CustomEvent(eventName, {
35
+ detail: {
36
+ toothId: this.toothId,
37
+ surface,
38
+ state: isSelected
39
+ },
40
+ bubbles: true,
41
+ composed: true
42
+ }));
43
+ }
44
+ render() {
45
+ return html`
46
+ <div class="tooth-container">
47
+ <span class="label">${this.toothId}</span>
48
+ <div class="tooth-relative">
49
+ <div class="surface top ${this.selections.vestibular ? "selected" : ""}"
50
+ @click=${() => this._toggle("vestibular", "vestibularC", "vestibularU")}></div>
51
+
52
+ <div class="surface left ${this.selections.distal ? "selected" : ""}"
53
+ @click=${() => this._toggle("distal", "distalC", "distalU")}></div>
54
+
55
+ <div class="surface bottom ${this.selections.palatine ? "selected" : ""}"
56
+ @click=${() => this._toggle("palatine", "palatineC", "palatineU")}></div>
57
+
58
+ <div class="surface right ${this.selections.mesial ? "selected" : ""}"
59
+ @click=${() => this._toggle("mesial", "mesialC", "mesialU")}></div>
60
+
61
+ <div class="surface center ${this.selections.occlusal ? "selected" : ""}"
62
+ @click=${() => this._toggle("occlusal", "occlusalC", "occlusalU")}></div>
63
+ </div>
64
+ </div>
65
+ `;
66
+ }
67
+ static {
68
+ this.styles = css`
69
+ .tooth-container { display: flex; flex-direction: column; align-items: center; width: 50px; }
70
+ .label { font-size: 12px; margin-bottom: 5px; font-weight: bold; color: #333; }
71
+ .tooth-relative { position: relative; width: 44px; height: 44px; background: #eee; }
72
+ .surface {
73
+ position: absolute; width: 20px; height: 20px; outline: 2px solid #000;
74
+ background-color: #fff; cursor: pointer; box-sizing: border-box; transition: background-color 0.2s;
75
+ }
76
+ .selected { background-color: var(--click-color, #ff6961) !important; }
77
+ .top { top: 0; left: 0; border-top-left-radius: 100%; }
78
+ .left { bottom: 0; left: 0; border-bottom-left-radius: 100%; }
79
+ .bottom { bottom: 0; right: 0; border-bottom-right-radius: 100%; }
80
+ .right { top: 0; right: 0; border-top-right-radius: 100%; }
81
+ .center { top: 25%; right: 25%; border-radius: 50%; z-index: 2; }
82
+ `;
83
+ }
84
+ };
85
+ __decorate([property({ type: String })], OgTooth.prototype, "toothId", void 0);
86
+ __decorate([property({ type: String })], OgTooth.prototype, "colorClick", void 0);
87
+ __decorate([property({ type: Object })], OgTooth.prototype, "selections", void 0);
88
+ OgTooth = __decorate([customElement("og-tooth")], OgTooth);
89
+
90
+ //#endregion
91
+ //#region src/og-odontogram.ts
92
+ let OgDontogram = class OgDontogram extends LitElement {
93
+ constructor(..._args) {
94
+ super(..._args);
95
+ this.mode = "adult";
96
+ this.chartData = {};
97
+ this.teethState = {};
98
+ this.layouts = {
99
+ adult: {
100
+ upperRight: [
101
+ 18,
102
+ 17,
103
+ 16,
104
+ 15,
105
+ 14,
106
+ 13,
107
+ 12,
108
+ 11
109
+ ],
110
+ upperLeft: [
111
+ 21,
112
+ 22,
113
+ 23,
114
+ 24,
115
+ 25,
116
+ 26,
117
+ 27,
118
+ 28
119
+ ],
120
+ lowerRight: [
121
+ 48,
122
+ 47,
123
+ 46,
124
+ 45,
125
+ 44,
126
+ 43,
127
+ 42,
128
+ 41
129
+ ],
130
+ lowerLeft: [
131
+ 31,
132
+ 32,
133
+ 33,
134
+ 34,
135
+ 35,
136
+ 36,
137
+ 37,
138
+ 38
139
+ ]
140
+ },
141
+ baby: {
142
+ upperRight: [
143
+ 55,
144
+ 54,
145
+ 53,
146
+ 52,
147
+ 51
148
+ ],
149
+ upperLeft: [
150
+ 61,
151
+ 62,
152
+ 63,
153
+ 64,
154
+ 65
155
+ ],
156
+ lowerRight: [
157
+ 85,
158
+ 84,
159
+ 83,
160
+ 82,
161
+ 81
162
+ ],
163
+ lowerLeft: [
164
+ 71,
165
+ 72,
166
+ 73,
167
+ 74,
168
+ 75
169
+ ]
170
+ },
171
+ old: {
172
+ upperRight: [
173
+ 17,
174
+ 16,
175
+ 15,
176
+ 14,
177
+ 13,
178
+ 12,
179
+ 11
180
+ ],
181
+ upperLeft: [
182
+ 21,
183
+ 22,
184
+ 23,
185
+ 24,
186
+ 25,
187
+ 26,
188
+ 27
189
+ ],
190
+ lowerRight: [
191
+ 47,
192
+ 46,
193
+ 45,
194
+ 44,
195
+ 43,
196
+ 42,
197
+ 41
198
+ ],
199
+ lowerLeft: [
200
+ 31,
201
+ 32,
202
+ 33,
203
+ 34,
204
+ 35,
205
+ 36,
206
+ 37
207
+ ]
208
+ }
209
+ };
210
+ }
211
+ willUpdate(changedProperties) {
212
+ if (changedProperties.has("chartData")) this.teethState = { ...this.chartData };
213
+ }
214
+ _updateState(id, region, value) {
215
+ const toothId = id.toString();
216
+ const newState = { ...this.teethState };
217
+ if (!newState[toothId]) newState[toothId] = {
218
+ vestibular: false,
219
+ distal: false,
220
+ palatine: false,
221
+ mesial: false,
222
+ occlusal: false
223
+ };
224
+ newState[toothId] = {
225
+ ...newState[toothId],
226
+ [region]: value
227
+ };
228
+ this.teethState = newState;
229
+ this.dispatchEvent(new CustomEvent("odontogram-change", {
230
+ detail: {
231
+ data: this.teethState,
232
+ mode: this.mode
233
+ },
234
+ bubbles: true,
235
+ composed: true
236
+ }));
237
+ }
238
+ renderTooth(id) {
239
+ const state = this.teethState[id.toString()] || {
240
+ vestibular: false,
241
+ distal: false,
242
+ palatine: false,
243
+ mesial: false,
244
+ occlusal: false
245
+ };
246
+ return html`
247
+ <og-tooth
248
+ .toothId=${id.toString()}
249
+ .selections=${state}
250
+ @vestibularC=${() => this._updateState(id, "vestibular", true)}
251
+ @vestibularU=${() => this._updateState(id, "vestibular", false)}
252
+ @distalC=${() => this._updateState(id, "distal", true)}
253
+ @distalU=${() => this._updateState(id, "distal", false)}
254
+ @palatineC=${() => this._updateState(id, "palatine", true)}
255
+ @palatineU=${() => this._updateState(id, "palatine", false)}
256
+ @mesialC=${() => this._updateState(id, "mesial", true)}
257
+ @mesialU=${() => this._updateState(id, "mesial", false)}
258
+ @occlusalC=${() => this._updateState(id, "occlusal", true)}
259
+ @occlusalU=${() => this._updateState(id, "occlusal", false)}
260
+ ></og-tooth>
261
+ `;
262
+ }
263
+ render() {
264
+ const layout = this.layouts[this.mode] || this.layouts.adult;
265
+ return html`
266
+ <div class="odontogram-wrapper mode-${this.mode}">
267
+ <div class="arch">
268
+ <div class="quadrant">${layout.upperRight.map((id) => this.renderTooth(id))}</div>
269
+ <div class="quadrant">${layout.upperLeft.map((id) => this.renderTooth(id))}</div>
270
+ </div>
271
+ <div class="arch">
272
+ <div class="quadrant">${layout.lowerRight.map((id) => this.renderTooth(id))}</div>
273
+ <div class="quadrant">${layout.lowerLeft.map((id) => this.renderTooth(id))}</div>
274
+ </div>
275
+ </div>
276
+ `;
277
+ }
278
+ static {
279
+ this.styles = css`
280
+ :host { display: block; padding: 20px; }
281
+ .odontogram-wrapper { display: flex; flex-direction: column; gap: 40px; align-items: center; }
282
+ .arch { display: flex; gap: 40px; }
283
+ .quadrant { display: flex; gap: 4px; }
284
+
285
+ /* You can add specific colors or spacing per mode if you want */
286
+ .mode-baby { gap: 20px; }
287
+ .mode-baby og-tooth { transform: scale(0.9); }
288
+ `;
289
+ }
290
+ };
291
+ __decorate([property({ type: String })], OgDontogram.prototype, "mode", void 0);
292
+ __decorate([property({ type: Object })], OgDontogram.prototype, "chartData", void 0);
293
+ __decorate([state()], OgDontogram.prototype, "teethState", void 0);
294
+ OgDontogram = __decorate([customElement("og-dontogram")], OgDontogram);
295
+
296
+ //#endregion
297
+ export { OgDontogram, OgTooth };
package/package.json CHANGED
@@ -1,120 +1,68 @@
1
1
  {
2
2
  "name": "odontogram",
3
- "version": "0.0.1",
4
- "description": "An Simple Web Component library for odontogram",
5
- "author": "Pratik Sharma <sharma.pratik2016@gmail.com>",
3
+ "version": "0.1.2",
4
+ "description": "A lightweight, interactive web component odontogram built with Lit.",
5
+ "main": "./dist/index.mjs",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.mts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.mts",
11
+ "import": "./dist/index.mjs"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
18
+ "sideEffects": [
19
+ "**/*.js",
20
+ "**/*.ts"
21
+ ],
22
+ "peerDependencies": {
23
+ "lit": "^3.0.0"
24
+ },
6
25
  "license": "MIT",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
7
29
  "keywords": [
8
30
  "odontogram",
9
31
  "react",
10
32
  "dental-chart"
11
33
  ],
12
- "publishConfig": {
13
- "access": "public"
14
- },
15
34
  "repository": {
16
35
  "type": "git",
17
36
  "url": "https://github.com/biomathcode/odontogram"
18
37
  },
19
- "main": "dist/index.js",
20
38
  "type": "module",
21
39
  "scripts": {
22
- "analyze": "cem analyze",
23
- "analyze:dev": "cem analyze --watch",
24
- "clean": "git clean -fqdx",
25
- "dev": "npm run build && concurrently -k -r \"npm run analyze:dev\" \"npm run build:watch\" \"npm run storybook\"",
26
- "test": "web-test-runner --group default",
27
- "build": "tsc && npm run analyze && npm run build:kitchen-sink",
28
- "build:cdn": "npx cross-env BUILD_TARGET=cdn vite build && npm run analyze",
29
- "build:html": "npx cross-env BUILD_TARGET=html vite build",
30
- "build:react": "npx cross-env BUILD_TARGET=react vite build && npx rimraf -rf ./public/react/html",
31
- "build:kitchen-sink": "npx rimraf ./public && npm run build:cdn && npm run build:html && npm run build:react",
32
- "build:watch": "concurrently -k -r \"tsc --watch\" \"npx cross-env BUILD_TARGET=cdn vite build --watch\"",
33
- "new": "plop",
34
- "deploy": "npm run build && npm publish",
35
- "format": "npm run format:eslint && npm run format:prettier",
36
- "format:eslint": "npx eslint --fix",
37
- "format:prettier": "npx prettier . --write",
38
- "lint": "wctools validate && npm run lint:eslint && npm run lint:prettier",
39
- "lint:eslint": "npx eslint",
40
- "lint:prettier": "npx prettier . --check",
41
- "prepare": "husky && npx playwright install-deps",
40
+ "dev": "vite",
41
+ "build": "tsdown",
42
+ "build:vite": "tsc && vite build",
43
+ "preview": "vite preview",
42
44
  "storybook": "storybook dev -p 6006",
43
- "build-storybook": "storybook build"
45
+ "build-storybook": "storybook build",
46
+ "deploy-storybook": "gh-pages -d storybook-static"
44
47
  },
45
48
  "dependencies": {
46
- "code-bubble": "^1.3.3",
47
- "lit": "^3.2.1",
48
- "wc-dox": "^1.3.5"
49
+ "lit": "^3.3.1"
49
50
  },
50
51
  "devDependencies": {
51
- "@custom-elements-manifest/analyzer": "^0.11.0",
52
- "@eslint/js": "^9.16.0",
53
- "@eslint/json": "^0.8.0",
54
- "@eslint/markdown": "^6.2.1",
55
- "@open-wc/testing": "^4.0.0",
56
- "@playwright/test": "^1.46.1",
57
- "@storybook/addon-a11y": "^10.1.11",
58
- "@storybook/addon-docs": "^10.1.11",
59
- "@storybook/addon-links": "^10.1.11",
60
- "@storybook/web-components": "^10.1.11",
61
- "@storybook/web-components-vite": "^10.1.11",
62
- "@types/mocha": "^10.0.2",
63
- "@wc-toolkit/cem-inheritance": "^1.2.2",
64
- "@wc-toolkit/cem-sorter": "^1.0.1",
65
- "@wc-toolkit/cem-validator": "^1.0.3",
66
- "@wc-toolkit/jsdoc-tags": "^1.1.0",
67
- "@wc-toolkit/jsx-types": "^1.5.2",
68
- "@wc-toolkit/lazy-loader": "^1.0.1",
69
- "@wc-toolkit/module-path-resolver": "^1.0.0",
70
- "@wc-toolkit/react-wrappers": "^1.1.1",
71
- "@wc-toolkit/storybook-helpers": "^10.0.0",
72
- "@wc-toolkit/type-parser": "^1.2.0",
73
- "@wc-toolkit/wctools": "^0.0.18",
74
- "@web/dev-server-esbuild": "^1.0.4",
75
- "@web/test-runner": "^0.20.2",
76
- "@web/test-runner-commands": "^0.9.0",
77
- "@web/test-runner-playwright": "^0.11.1",
78
- "concurrently": "^9.1.0",
79
- "custom-element-svelte-integration": "^1.1.2",
80
- "custom-element-vuejs-integration": "^1.3.3",
81
- "custom-elements-manifest-deprecator": "^1.1.1",
82
- "eslint": "^9.16.0",
83
- "eslint-config-prettier": "^9.1.0",
84
- "eslint-plugin-lit": "^1.15.0",
85
- "eslint-plugin-lit-a11y": "^5.1.1",
86
- "eslint-plugin-require-extensions": "^0.1.3",
87
- "eslint-plugin-storybook": "^10.1.11",
88
- "glob": "^11.0.0",
89
- "globals": "^15.13.0",
90
- "husky": "^9.0.11",
91
- "lint-staged": "^15.2.7",
92
- "plop": "^4.0.1",
93
- "prettier": "^3.3.2",
94
- "rollup-plugin-summary": "^3.0.0",
95
- "storybook": "^10.1.11",
96
- "typescript": "^5.5.3",
97
- "typescript-eslint": "^8.17.0",
98
- "vite": "^7.3.0",
99
- "vite-plugin-dts": "^4.5.4"
100
- },
101
- "lint-staged": {
102
- "*.js": "eslint --cache --fix",
103
- "*.format:prettier": "prettier --write"
104
- },
105
- "files": [
106
- "cdn",
107
- "eslint",
108
- "dist",
109
- "react",
110
- "types",
111
- "index.d.ts",
112
- "index.js",
113
- "package.json",
114
- "custom-elements.json",
115
- "vscode.css-custom-data.json",
116
- "vscode.html-custom-data.json",
117
- "web-types.json"
118
- ],
119
- "customElements": "custom-elements.json"
52
+ "@chromatic-com/storybook": "^5.0.1",
53
+ "@storybook/addon-a11y": "^10.2.13",
54
+ "@storybook/addon-docs": "^10.2.13",
55
+ "@storybook/addon-vitest": "^10.2.13",
56
+ "@storybook/web-components-vite": "^10.2.13",
57
+ "@vitest/browser-playwright": "^4.0.18",
58
+ "@vitest/coverage-v8": "^4.0.18",
59
+ "gh-pages": "^6.3.0",
60
+ "playwright": "^1.58.2",
61
+ "storybook": "^10.2.13",
62
+ "tsdown": "0.21.0-beta.2",
63
+ "typescript": "~5.9.3",
64
+ "vite": "^7.3.1",
65
+ "vite-plugin-dts": "^4.5.4",
66
+ "vitest": "^4.0.18"
67
+ }
120
68
  }
package/readme.md ADDED
@@ -0,0 +1,140 @@
1
+ # 🦷 Og-odontogram
2
+
3
+ A lightweight, interactive, and highly customizable **Web Component Ogdontogram** built with [Lit](https://lit.dev/). Perfect for dental software, patient records, and educational tools.
4
+
5
+ ## ✨ Features
6
+
7
+ * **Zero Framework Dependency**: Works with React, Vue, Angular, or plain HTML.
8
+ * **Multi-Mode Support**: Toggle between `adult` (32 teeth), `baby` (20 primary teeth), and `old` (geriatric) layouts.
9
+ * **Interactive Regions**: Supports 5 surfaces per tooth: Vestibular, Distal, Palatine, Mesial, and Occlusal.
10
+ * **JSON Powered**: Export and rehydrate the entire chart state via a simple JSON object.
11
+ * **CSS Theming**: Customize selection colors using CSS variables.
12
+ * **Open-WC Compliant**: Shipped as unoptimized ESM for maximum bundler compatibility.
13
+
14
+ ---
15
+
16
+ ## 📦 Installation
17
+
18
+ Install via NPM:
19
+
20
+ ```bash
21
+ npm install odontogram
22
+
23
+ ```
24
+
25
+ ---
26
+
27
+ ## 🚀 Quick Start
28
+
29
+ ### Plain HTML / Vanilla JS
30
+
31
+ ```html
32
+ <script type="module">
33
+ import 'odontogram';
34
+ </script>
35
+
36
+ <og-odontogram id="my-chart" mode="adult"></og-odontogram>
37
+
38
+ <script>
39
+ const chart = document.getElementById('my-chart');
40
+
41
+ // Listen for changes
42
+ chart.addEventListener('oodontogram-change', (e) => {
43
+ console.log('New State:', e.detail.data);
44
+ });
45
+ </script>
46
+
47
+ ```
48
+
49
+ ### React / Next.js
50
+
51
+ ```jsx
52
+ import 'oodontogram';
53
+
54
+ function App() {
55
+ return (
56
+ <og-odontogram
57
+ mode="baby"
58
+ onoodontogram-change={(e) => console.log(e.detail.data)}
59
+ />
60
+ );
61
+ }
62
+
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 🛠 API & Configuration
68
+
69
+ ### Properties
70
+
71
+ | Property | Type | Default | Description |
72
+ | ----------- | -------- | --------- | ---------------------------------------- |
73
+ | `mode` | `string` | `'adult'` | Patient type: `adult`, `baby`, or `old`. |
74
+ | `chartData` | `object` | `{}` | Initial state to pre-fill the chart. |
75
+
76
+ ### Custom Events
77
+
78
+ | Event | Detail | Description |
79
+ | -------------------- | --------------------------- | ------------------------------------------ |
80
+ | `oodontogram-change` | `{ data: {}, mode: string}` | Fired whenever a tooth surface is toggled. |
81
+
82
+ ### CSS Variables
83
+
84
+ Customize the look of the selected regions:
85
+
86
+ ```css
87
+ og-odontogram {
88
+ --click-color: #ff6961; /* The color of selected surfaces */
89
+ }
90
+
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 📊 Data Structure
96
+
97
+ The component exports a clean JSON structure representing the "history" or "state" of the teeth.
98
+
99
+ ```json
100
+ {
101
+ "16": {
102
+ "vestibular": true,
103
+ "distal": false,
104
+ "palatine": false,
105
+ "mesial": true,
106
+ "occlusal": true
107
+ }
108
+ }
109
+
110
+ ```
111
+
112
+ ---
113
+
114
+ ## 🎨 Storybook (Development & Demo)
115
+
116
+ We use Storybook to showcase all modes and test interactivity. You can see the "Adult", "Pediatric", and "Senior" layouts in isolation.
117
+
118
+ To run Storybook locally:
119
+
120
+ 1. Clone the repo.
121
+ 2. `npm install`
122
+ 3. `npm run storybook`
123
+
124
+ ---
125
+
126
+ ## 🏗 Developing
127
+
128
+ If you want to contribute or build from source:
129
+
130
+ * **Build for Production**: `npm run build` (uses `tsdown` to generate `.mjs` and `.d.mts`).
131
+ * **Test**: `npm run test` (uses Vitest + Playwright for browser testing).
132
+ * **Lint**: `npm run lint`.
133
+
134
+ ## 📄 License
135
+
136
+ MIT © [Biomathcode](https://github.com/biomathcode)
137
+
138
+ ---
139
+
140
+ **Would you like me to help you set up a GitHub Action to automatically publish this to NPM whenever you create a new release?**
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './og-tooth';
2
+ export * from './og-odontogram';