ark-meta-tags 0.0.1
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.
- package/README.md +579 -0
- package/ark-meta-tags-test.js +640 -0
- package/ark-meta-tags.js +1083 -0
- package/demo.html +1370 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
# π· ArkMetaTags
|
|
2
|
+
|
|
3
|
+
> A self-contained, zero-dependency multi-instance metadata tag-capture widget.
|
|
4
|
+
> Built for **News Trace** β ARK: Immanuel project.
|
|
5
|
+
|
|
6
|
+
[](https://github.com/your-org/ark-meta-tags)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](#)
|
|
9
|
+
[](#installation)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
ArkMetaTags renders a tag-chip strip. Tapping any tag adds an entry below; tapping again adds another instance of the **same tag**. This makes it ideal for recording sequential events against a single record β multiple court hearings, multiple follow-up dates, multiple witnesses β each with its own note.
|
|
16
|
+
|
|
17
|
+
## Demo Link
|
|
18
|
+
|
|
19
|
+
You may be using [Demo Link](https://ark-meta-tags.immanuel.co/demo.html).
|
|
20
|
+
|
|
21
|
+
**Key features**
|
|
22
|
+
|
|
23
|
+
| Feature | Detail |
|
|
24
|
+
|---|---|
|
|
25
|
+
| 16 built-in tags | Contact, Phone, FIR, IPC Section, Officer, Victim, Witness, Accused, Case Status, Court/Hearing, Hospital, Source, Reference, Follow-up Date, Note, Custom Tag |
|
|
26
|
+
| 6 field types | `text`, `textarea`, `select`, `chiplist`, `keyvalue`, `dateNote` |
|
|
27
|
+
| `hasNote` flag | Adds an *action / note* sub-row to any simple type |
|
|
28
|
+
| `hasDate` flag | Adds a date picker sub-row to any simple type |
|
|
29
|
+
| `fields[]` array | Full multi-field control per instance |
|
|
30
|
+
| Custom `tagDefs` | Extend or fully replace the 16 built-in tags |
|
|
31
|
+
| `collectData()` | Returns structured JSON β one array per tag key |
|
|
32
|
+
| `loadData(meta)` | Restores widget from a previous `collectData()` output |
|
|
33
|
+
| DOM events | `ark-meta:add`, `ark-meta:remove`, `ark-meta:change` |
|
|
34
|
+
| Self-contained CSS | Injects `<style>` into `<head>` once, no external sheet needed |
|
|
35
|
+
| UMD build | Works as `<script>`, `require()`, or `import` |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## File structure
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
ark-meta-tags.js β Main library
|
|
43
|
+
ark-test-data.js β Companion test-data generator (optional)
|
|
44
|
+
ark-meta-tags-docs.htmlβ Interactive documentation page
|
|
45
|
+
cases-list.html β Case review list (approve / reject / invalid)
|
|
46
|
+
news-trace-demo.html β News Trace entry form using the library
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
### Script tag
|
|
54
|
+
```html
|
|
55
|
+
<script src="ark-meta-tags.js"></script>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### ES Module
|
|
59
|
+
```js
|
|
60
|
+
import ArkMetaTags from './ark-meta-tags.js';
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### CommonJS
|
|
64
|
+
```js
|
|
65
|
+
const ArkMetaTags = require('./ark-meta-tags');
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
No npm, no bundler required.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Quick Start
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<!-- Host element -->
|
|
76
|
+
<div id="meta-host"></div>
|
|
77
|
+
|
|
78
|
+
<script src="ark-meta-tags.js"></script>
|
|
79
|
+
<script>
|
|
80
|
+
const widget = new ArkMetaTags('#meta-host', {
|
|
81
|
+
onChange(data) {
|
|
82
|
+
console.log(data); // fires on every add / remove
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Collect structured JSON
|
|
87
|
+
const meta = widget.collectData();
|
|
88
|
+
|
|
89
|
+
// Restore from saved data
|
|
90
|
+
widget.loadData(savedMeta);
|
|
91
|
+
</script>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> **No CSS needed on the host element.** The library writes its own `<style>` once. Override with `--amt-font` CSS variable.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Tag Types
|
|
99
|
+
|
|
100
|
+
Every tag definition requires a `type` that controls the rendered input and the shape of each `collectData()` entry.
|
|
101
|
+
|
|
102
|
+
### `text`
|
|
103
|
+
Single-line text input.
|
|
104
|
+
```js
|
|
105
|
+
{ key: 'witness', label: 'Witness', icon: `β¦`,
|
|
106
|
+
type: 'text', ph: 'Witness name and accountβ¦' }
|
|
107
|
+
// collectData entry: { value: "β¦" }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `textarea`
|
|
111
|
+
Resizable multiline text area.
|
|
112
|
+
```js
|
|
113
|
+
{ key: 'note', label: 'Internal Note', icon: `β¦`,
|
|
114
|
+
type: 'textarea', ph: 'Off-record notesβ¦' }
|
|
115
|
+
// collectData entry: { value: "β¦" }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### `select`
|
|
119
|
+
Dropdown from a fixed options list. The first option is treated as a blank placeholder.
|
|
120
|
+
```js
|
|
121
|
+
{ key: 'status', label: 'Case Status', icon: `β¦`,
|
|
122
|
+
type: 'select',
|
|
123
|
+
opts: ['Select statusβ¦', 'Under investigation', 'FIR registered', 'In court'] }
|
|
124
|
+
// collectData entry: { value: "Under investigation" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `chiplist`
|
|
128
|
+
Multiple values entered as chips. Press **Enter** or **,** to commit each chip. Backspace removes the last chip.
|
|
129
|
+
```js
|
|
130
|
+
{ key: 'phone', label: 'Phone', icon: `β¦`,
|
|
131
|
+
type: 'chiplist', ph: 'Type number, press Enterβ¦' }
|
|
132
|
+
// collectData entry: { values: ["+91 9876543210", "+91 9898989898"] }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `keyvalue`
|
|
136
|
+
Side-by-side label : value pair. Left field is the key/label, right field is the value.
|
|
137
|
+
```js
|
|
138
|
+
{ key: 'custom', label: 'Custom Tag', icon: `β¦`,
|
|
139
|
+
type: 'keyvalue', phKey: 'Field nameβ¦', phVal: 'Valueβ¦' }
|
|
140
|
+
// collectData entry: { key: "RTI Ref", value: "RTI/221/2025" }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `dateNote`
|
|
144
|
+
Date picker as the primary input with a mandatory note textarea always rendered below it.
|
|
145
|
+
```js
|
|
146
|
+
{ key: 'followup', label: 'Follow-up Date', icon: `β¦`,
|
|
147
|
+
type: 'dateNote', ph: 'What to follow up onβ¦' }
|
|
148
|
+
// collectData entry: { date: "2025-06-01", note: "Check FIR status" }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## `hasNote` and `hasDate` Flags
|
|
154
|
+
|
|
155
|
+
Add optional sub-rows to any simple `type` without switching to multi-field mode.
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
// hasNote β appends "Action / note" textarea below the main input
|
|
159
|
+
{ key: 'fir', label: 'FIR No.', icon: `β¦`,
|
|
160
|
+
type: 'text', ph: 'FIR No. 000/2025β¦',
|
|
161
|
+
hasNote: true }
|
|
162
|
+
// β { value: "FIR No. 047/2025", note: "IPC 302, 307" }
|
|
163
|
+
|
|
164
|
+
// hasDate β appends a date picker below the main input
|
|
165
|
+
{ key: 'officer', label: 'Officer / Official', icon: `β¦`,
|
|
166
|
+
type: 'text', ph: 'Name, rank, departmentβ¦',
|
|
167
|
+
hasDate: true }
|
|
168
|
+
// β { value: "SP Rajesh Kumar", date: "2025-05-13" }
|
|
169
|
+
|
|
170
|
+
// Both together
|
|
171
|
+
{ key: 'accused', label: 'Accused', icon: `β¦`,
|
|
172
|
+
type: 'text', ph: 'Name(s) of accusedβ¦',
|
|
173
|
+
hasDate: true, hasNote: true }
|
|
174
|
+
// β { value: "β¦", date: "2025-03-10", note: "Arrested; currently in judicial custody" }
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Multi-field (`fields[]`)
|
|
180
|
+
|
|
181
|
+
For tags that need several named sub-fields per instance, replace `type` with a `fields` array.
|
|
182
|
+
|
|
183
|
+
| Field type | Renders as |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `text` | Single-line input (primary row or labelled secondary row) |
|
|
186
|
+
| `date` | Date picker in a date-row style |
|
|
187
|
+
| `note` | Textarea in a note-row style (italic, subdued background) |
|
|
188
|
+
| `chiplist` | Chip input |
|
|
189
|
+
| `select` | Dropdown |
|
|
190
|
+
| `textarea` | Multiline input |
|
|
191
|
+
|
|
192
|
+
```js
|
|
193
|
+
{
|
|
194
|
+
key: 'court', label: 'Court / Hearing', icon: `β¦`,
|
|
195
|
+
fields: [
|
|
196
|
+
{ name: 'court', type: 'text', ph: 'Court nameβ¦' },
|
|
197
|
+
{ name: 'date', type: 'date', label: 'Hearing date' },
|
|
198
|
+
{ name: 'judge', type: 'text', ph: 'Presiding judgeβ¦',
|
|
199
|
+
label: 'Judge' },
|
|
200
|
+
{ name: 'outcome', type: 'note', ph: 'What happenedβ¦' },
|
|
201
|
+
{ name: 'links', type: 'chiplist', ph: 'Paste URL, Enterβ¦' },
|
|
202
|
+
{ name: 'verdict', type: 'select',
|
|
203
|
+
opts: ['Selectβ¦', 'Pending', 'Convicted', 'Acquitted'] },
|
|
204
|
+
],
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// collectData() entry per instance:
|
|
208
|
+
// {
|
|
209
|
+
// court: "Madras High Court",
|
|
210
|
+
// date: "2025-05-13",
|
|
211
|
+
// judge: "Hon. Justice S. Ramakrishnan",
|
|
212
|
+
// outcome: "Witness did not appear; hearing adjourned",
|
|
213
|
+
// links: ["https://ecourts.gov.in/β¦"],
|
|
214
|
+
// verdict: "Pending"
|
|
215
|
+
// }
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Multiple instances example** β three separate court hearings on one case:
|
|
219
|
+
```js
|
|
220
|
+
widget.collectData();
|
|
221
|
+
// {
|
|
222
|
+
// court: [
|
|
223
|
+
// { court: "Chennai HC", date: "2025-03-10", outcome: "Bail rejected" },
|
|
224
|
+
// { court: "Chennai HC", date: "2025-05-13", outcome: "Witness absent" },
|
|
225
|
+
// { court: "Chennai HC", date: "2025-07-22", outcome: "Judge reserved" },
|
|
226
|
+
// ]
|
|
227
|
+
// }
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Custom `tagDefs`
|
|
233
|
+
|
|
234
|
+
### Merge mode (default β `replaceTagDefs: false`)
|
|
235
|
+
|
|
236
|
+
User-supplied entries override built-ins by matching `key`. Unknown keys are appended to the end of the strip.
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
const widget = new ArkMetaTags('#host', {
|
|
240
|
+
tagDefs: [
|
|
241
|
+
// New tag β appended to the strip
|
|
242
|
+
{
|
|
243
|
+
key: 'journalist', label: 'Journalist',
|
|
244
|
+
icon: `<path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4z"/>`,
|
|
245
|
+
type: 'text', ph: 'Reporter name & outletβ¦',
|
|
246
|
+
},
|
|
247
|
+
// Override built-in 'court' with multi-field version
|
|
248
|
+
{
|
|
249
|
+
key: 'court',
|
|
250
|
+
fields: [
|
|
251
|
+
{ name: 'court', type: 'text', ph: 'Court nameβ¦' },
|
|
252
|
+
{ name: 'date', type: 'date', label: 'Hearing date' },
|
|
253
|
+
{ name: 'note', type: 'note', ph: 'What happenedβ¦' },
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Replace mode (`replaceTagDefs: true`)
|
|
261
|
+
|
|
262
|
+
Skips all 16 built-ins entirely β use only your definitions.
|
|
263
|
+
|
|
264
|
+
```js
|
|
265
|
+
const widget = new ArkMetaTags('#host', {
|
|
266
|
+
replaceTagDefs: true,
|
|
267
|
+
tagDefs: [
|
|
268
|
+
{ key: 'sku', label: 'SKU', icon: `β¦`, type: 'text' },
|
|
269
|
+
{ key: 'colour', label: 'Colour', icon: `β¦`, type: 'chiplist' },
|
|
270
|
+
{ key: 'expiry', label: 'Expiry', icon: `β¦`, type: 'dateNote' },
|
|
271
|
+
],
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Options Reference
|
|
278
|
+
|
|
279
|
+
```js
|
|
280
|
+
new ArkMetaTags(container, {
|
|
281
|
+
tagDefs : [], // custom tag definitions (see above)
|
|
282
|
+
replaceTagDefs: false, // true = use tagDefs only, ignore built-ins
|
|
283
|
+
onChange : meta => {}, // called on add/remove with full collectData() object
|
|
284
|
+
sectionTitle : 'Details β tap to add', // text in the ornamental divider
|
|
285
|
+
hint : 'Tap a tag to addβ¦', // italic hint below divider
|
|
286
|
+
showRule : true, // show/hide the ornamental divider
|
|
287
|
+
injectStyles : true, // auto-inject library CSS into <head>
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
| Option | Type | Default | Description |
|
|
292
|
+
|---|---|---|---|
|
|
293
|
+
| `tagDefs` | `Array` | `null` | Custom tag definitions |
|
|
294
|
+
| `replaceTagDefs` | `boolean` | `false` | Skip built-in tags entirely |
|
|
295
|
+
| `onChange` | `function` | `null` | Structural-change callback |
|
|
296
|
+
| `sectionTitle` | `string` | `'Details β tap to add'` | Section-rule label |
|
|
297
|
+
| `hint` | `string` | `'Tap a tag to addβ¦'` | Italic hint text |
|
|
298
|
+
| `showRule` | `boolean` | `true` | Show ornamental divider |
|
|
299
|
+
| `injectStyles` | `boolean` | `true` | Auto-inject CSS |
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Methods
|
|
304
|
+
|
|
305
|
+
```js
|
|
306
|
+
const w = new ArkMetaTags('#host', { /* options */ });
|
|
307
|
+
|
|
308
|
+
// Read all entries as structured JSON
|
|
309
|
+
const data = w.collectData();
|
|
310
|
+
// β { court: [{value,note}], phone: [{values:[β¦]}], followup: [{date,note}], β¦ }
|
|
311
|
+
|
|
312
|
+
// Restore from a previous collectData() result (clears first)
|
|
313
|
+
w.loadData(data);
|
|
314
|
+
|
|
315
|
+
// Programmatically add one instance of a tag
|
|
316
|
+
w.addTag('court'); // same as clicking the "Court / Hearing" chip
|
|
317
|
+
|
|
318
|
+
// Clear all entries and groups
|
|
319
|
+
w.reset();
|
|
320
|
+
|
|
321
|
+
// Unmount β empties the container element
|
|
322
|
+
w.destroy();
|
|
323
|
+
|
|
324
|
+
// Static properties
|
|
325
|
+
ArkMetaTags.version; // '1.0.0'
|
|
326
|
+
ArkMetaTags.defaultTagDefs; // array of 16 built-in definitions
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Events
|
|
332
|
+
|
|
333
|
+
All events are dispatched on the **host container element** with `bubbles: true`.
|
|
334
|
+
|
|
335
|
+
| Event | `e.detail` | Fired when |
|
|
336
|
+
|---|---|---|
|
|
337
|
+
| `ark-meta:add` | `{ key, iid, instance }` | An instance is added |
|
|
338
|
+
| `ark-meta:remove` | `{ key, iid, instance }` | An instance is removed |
|
|
339
|
+
| `ark-meta:change` | `{ data, instance }` | Any structural change (add/remove/reset) |
|
|
340
|
+
|
|
341
|
+
`e.detail.instance` is the live `ArkMetaTags` object β call any method from inside a listener.
|
|
342
|
+
|
|
343
|
+
```js
|
|
344
|
+
const host = document.getElementById('meta-host');
|
|
345
|
+
const w = new ArkMetaTags(host, {
|
|
346
|
+
onChange(data) {
|
|
347
|
+
// Fires on every add / remove / reset
|
|
348
|
+
saveToServer(data);
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Or listen via DOM:
|
|
353
|
+
host.addEventListener('ark-meta:add', e => {
|
|
354
|
+
console.log('Added tag:', e.detail.key, 'instance id:', e.detail.iid);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
host.addEventListener('ark-meta:remove', e => {
|
|
358
|
+
console.log('Removed:', e.detail.key);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
host.addEventListener('ark-meta:change', e => {
|
|
362
|
+
// e.detail.data = full collectData() object
|
|
363
|
+
console.log(e.detail.data);
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## `collectData()` Format Reference
|
|
370
|
+
|
|
371
|
+
```js
|
|
372
|
+
{
|
|
373
|
+
// text / select / textarea β { value, [note], [date] }
|
|
374
|
+
witness : [{ value: "Arjun Kumar β Eyewitness" }],
|
|
375
|
+
fir : [{ value: "FIR No. 047/2025", note: "IPC 302, 307" }],
|
|
376
|
+
officer : [{ value: "SP Rajesh Kumar", date: "2025-05-13", note: "Present at scene" }],
|
|
377
|
+
status : [{ value: "In court / Trial", note: "Fast-track court hearing scheduled" }],
|
|
378
|
+
|
|
379
|
+
// chiplist β { values: string[] }
|
|
380
|
+
phone : [{ values: ["+91 9876543210", "+91 9898989898"] }],
|
|
381
|
+
ref : [{ values: ["https://thehindu.com/β¦", "https://ecourts.gov.in/β¦"] }],
|
|
382
|
+
|
|
383
|
+
// keyvalue β { key, value }
|
|
384
|
+
custom : [{ key: "RTI Ref No.", value: "RTI/221/2025" },
|
|
385
|
+
{ key: "Editor Note", value: "Hold for legal clearance" }],
|
|
386
|
+
|
|
387
|
+
// dateNote β { date, note }
|
|
388
|
+
followup : [{ date: "2025-06-01", note: "Chase FIR status from PS" },
|
|
389
|
+
{ date: "2025-07-22", note: "Attend court hearing" }],
|
|
390
|
+
|
|
391
|
+
// fields[] β flat object keyed by field name
|
|
392
|
+
court : [
|
|
393
|
+
{ court: "Madras HC", date: "2025-03-10", judge: "Hon. Justice S.R.",
|
|
394
|
+
outcome: "Bail rejected", links: ["https://β¦"], verdict: "Pending" },
|
|
395
|
+
{ court: "Madras HC", date: "2025-05-13", judge: "Hon. Justice S.R.",
|
|
396
|
+
outcome: "Case reserved for judgment", links: [], verdict: "Reserved" },
|
|
397
|
+
],
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Styling & CSS Variables
|
|
404
|
+
|
|
405
|
+
```css
|
|
406
|
+
/* Override font */
|
|
407
|
+
.amt-widget { --amt-font: 'Your Font', sans-serif; }
|
|
408
|
+
|
|
409
|
+
/* Key classes for targeted overrides */
|
|
410
|
+
.amt-strip { /* tag chip row */ }
|
|
411
|
+
.amt-tag { /* individual chip */ }
|
|
412
|
+
.amt-tag-count { /* instance count badge */ }
|
|
413
|
+
.amt-group { /* all instances of one tag */ }
|
|
414
|
+
.amt-group-head { /* group header (label + "Add another" button) */ }
|
|
415
|
+
.amt-inst { /* one instance row */ }
|
|
416
|
+
.amt-inst-num { /* instance number badge */ }
|
|
417
|
+
.amt-note-row { /* note sub-row */ }
|
|
418
|
+
.amt-date-row { /* date sub-row */ }
|
|
419
|
+
.amt-chip { /* chip in a chiplist */ }
|
|
420
|
+
.amt-kv-key { /* key field of keyvalue */ }
|
|
421
|
+
.amt-kv-val { /* value field of keyvalue */ }
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
All library classes are prefixed `amt-` to avoid collision with host page styles.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## ArkTestData β Companion Test Library
|
|
429
|
+
|
|
430
|
+
`ark-test-data.js` generates realistic Indian news-reporter data to seed and test the widget.
|
|
431
|
+
|
|
432
|
+
### Quick usage
|
|
433
|
+
|
|
434
|
+
```js
|
|
435
|
+
// Generate and load into widget
|
|
436
|
+
widget.loadData(ArkTestData.generateMeta());
|
|
437
|
+
|
|
438
|
+
// Generate a full case object
|
|
439
|
+
const c = ArkTestData.generateCase();
|
|
440
|
+
// β { id, title, priority, category, location, meta, body, reporter, β¦ }
|
|
441
|
+
|
|
442
|
+
// Generate N cases with unique IDs
|
|
443
|
+
const cases = ArkTestData.generateCases(10);
|
|
444
|
+
|
|
445
|
+
// Seed localStorage (used by cases-list.html)
|
|
446
|
+
ArkTestData.seedLocalStorage(20); // append 20 cases
|
|
447
|
+
ArkTestData.seedLocalStorage(5, true); // replace with 5
|
|
448
|
+
ArkTestData.clearLocalStorage();
|
|
449
|
+
|
|
450
|
+
// Summary table in console
|
|
451
|
+
ArkTestData.consoleReport(cases);
|
|
452
|
+
|
|
453
|
+
// Use raw corpus for custom generators
|
|
454
|
+
const city = ArkTestData.rng.pick(ArkTestData.corpus.CITIES);
|
|
455
|
+
const phone = ArkTestData.rng.phone();
|
|
456
|
+
const name = ArkTestData.rng.name();
|
|
457
|
+
const date = ArkTestData.rng.isoDate(365); // random past date β€ 1 year ago
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Data generated per tag key
|
|
461
|
+
|
|
462
|
+
| Tag key | Generated shape |
|
|
463
|
+
|---|---|
|
|
464
|
+
| `contact` | `{ value: "Priya Krishnan" }` |
|
|
465
|
+
| `phone` | `{ values: ["+91 9876543210"] }` |
|
|
466
|
+
| `fir` | `{ value: "FIR No. 047/2025 β Central PS", note: "IPC 302, 307" }` |
|
|
467
|
+
| `section` | `{ value: "IPC 302, BNS 101" }` |
|
|
468
|
+
| `officer` | 1β3 instances β `{ value: "SP Rajesh Kumar β Tamil Nadu Police", note: "β¦" }` |
|
|
469
|
+
| `victim` | 1β2 instances β `{ value: "Arjun Kumar, 34 yrs, Male", note: "Admitted to Govt. General Hospital" }` |
|
|
470
|
+
| `witness` | `{ value: "Sunita Rao β Eyewitness", note: "Identity withheld" }` |
|
|
471
|
+
| `accused` | 1β3 instances β `{ value: "Mohan Raj", note: "Arrested; in judicial custody" }` |
|
|
472
|
+
| `status` | `{ value: "In court / Trial", note: "Last updated: 2025-03-10" }` |
|
|
473
|
+
| `court` | 1β4 instances, each one hearing β `{ value: "Chennai HC Β· 13 May", note: "Witness absent" }` |
|
|
474
|
+
| `hospital` | `{ value: "Govt. General Hospital", note: "Condition: Stable" }` |
|
|
475
|
+
| `source` | `{ value: "Eyewitness", note: "Contact: +91 β¦" }` |
|
|
476
|
+
| `ref` | `{ values: ["https://thehindu.com/β¦", "https://scroll.in/β¦"] }` |
|
|
477
|
+
| `followup` | 1β3 instances β `{ date: "2025-07-01", note: "Chase FIR status from PS" }` |
|
|
478
|
+
| `note` | `{ value: "Off-record: source requested anonymity" }` |
|
|
479
|
+
| `custom` | 1β2 instances β `{ key: "RTI Ref No.", value: "RTI/221/2025" }` |
|
|
480
|
+
|
|
481
|
+
### Smoke test
|
|
482
|
+
|
|
483
|
+
```js
|
|
484
|
+
const { pass, fail } = ArkTestData.smokeTest();
|
|
485
|
+
// Logs ~20 β/β assertions covering:
|
|
486
|
+
// - case object shape (id, title, priority, reviewStatus, location, meta, body, created)
|
|
487
|
+
// - meta key shapes (phone.values, court.note, custom.key, followup.date)
|
|
488
|
+
// - generateCases(5) unique IDs
|
|
489
|
+
// - localStorage seed, read, clear cycle
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Integration Test Pattern
|
|
495
|
+
|
|
496
|
+
```js
|
|
497
|
+
function integrationTest(widget) {
|
|
498
|
+
// 1. Generate random meta
|
|
499
|
+
const meta = ArkTestData.generateMeta();
|
|
500
|
+
|
|
501
|
+
// 2. Load into widget
|
|
502
|
+
widget.loadData(meta);
|
|
503
|
+
|
|
504
|
+
// 3. Collect back
|
|
505
|
+
const out = widget.collectData();
|
|
506
|
+
|
|
507
|
+
// 4. Assert shapes match what was loaded
|
|
508
|
+
if (meta.court && out.court) {
|
|
509
|
+
console.assert(out.court.length === meta.court.length, 'court entry count matches');
|
|
510
|
+
console.assert('value' in out.court[0], 'court[0] has value');
|
|
511
|
+
console.assert('note' in out.court[0], 'court[0] has note');
|
|
512
|
+
}
|
|
513
|
+
if (meta.phone) {
|
|
514
|
+
console.assert(Array.isArray(out.phone[0].values), 'phone values is array');
|
|
515
|
+
}
|
|
516
|
+
if (meta.custom) {
|
|
517
|
+
console.assert(out.custom[0].key === meta.custom[0].key, 'custom key round-trips');
|
|
518
|
+
console.assert(out.custom[0].value === meta.custom[0].value, 'custom value round-trips');
|
|
519
|
+
}
|
|
520
|
+
if (meta.followup) {
|
|
521
|
+
console.assert(out.followup[0].date === meta.followup[0].date, 'followup date round-trips');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// 5. Reset and verify empty
|
|
525
|
+
widget.reset();
|
|
526
|
+
const empty = widget.collectData();
|
|
527
|
+
console.assert(Object.keys(empty).length === 0, 'reset gives empty collectData()');
|
|
528
|
+
|
|
529
|
+
console.log('Integration test: PASS');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
integrationTest(new ArkMetaTags('#test-host'));
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Browser Support
|
|
538
|
+
|
|
539
|
+
| Chrome | Firefox | Safari | Edge |
|
|
540
|
+
|:---:|:---:|:---:|:---:|
|
|
541
|
+
| β
80+ | β
75+ | β
13.1+ | β
80+ |
|
|
542
|
+
|
|
543
|
+
Requires: `CustomEvent`, `IntersectionObserver` (docs page only), `navigator.clipboard` (copy button only).
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## File List
|
|
548
|
+
|
|
549
|
+
| File | Purpose |
|
|
550
|
+
|---|---|
|
|
551
|
+
| `ark-meta-tags.js` | Library β mount with `new ArkMetaTags(el, options)` |
|
|
552
|
+
| `ark-test-data.js` | Test helper β `ArkTestData.generateMeta()`, `seedLocalStorage()`, `smokeTest()` |
|
|
553
|
+
| `ark-meta-tags-docs.html` | Interactive documentation with all features and live demos |
|
|
554
|
+
| `news-trace-demo.html` | News Trace entry form β real-world usage of the library |
|
|
555
|
+
| `cases-list.html` | Responsive case list with approve / reject / invalid review actions |
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## Changelog
|
|
560
|
+
|
|
561
|
+
### v1.0.0
|
|
562
|
+
- Initial release extracted from News Trace project
|
|
563
|
+
- 16 built-in news-reporter tag definitions
|
|
564
|
+
- 6 field types: `text`, `textarea`, `select`, `chiplist`, `keyvalue`, `dateNote`
|
|
565
|
+
- `hasNote` and `hasDate` flags for simple types
|
|
566
|
+
- `fields[]` multi-field mode per tag
|
|
567
|
+
- `collectData()` / `loadData()` round-trip
|
|
568
|
+
- DOM events: `ark-meta:add`, `ark-meta:remove`, `ark-meta:change`
|
|
569
|
+
- `onChange` callback option
|
|
570
|
+
- `addTag()`, `reset()`, `destroy()` public methods
|
|
571
|
+
- `ArkMetaTags.defaultTagDefs` static accessor
|
|
572
|
+
- UMD wrapper (script tag / require / import)
|
|
573
|
+
- Self-injecting CSS with `amt-` namespace prefix
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## License
|
|
578
|
+
|
|
579
|
+
MIT Β© ARK: Immanuel β https://immanuel.co
|