@fieldia/chatter 0.8.0

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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/package.json +39 -0
  4. package/src/index.d.mts +6 -0
  5. package/src/index.d.ts +6 -0
  6. package/src/index.js +10 -0
  7. package/src/index.js.map +1 -0
  8. package/src/index.mjs +7 -0
  9. package/src/index.mjs.map +1 -0
  10. package/src/lib/activities.d.mts +19 -0
  11. package/src/lib/activities.d.ts +19 -0
  12. package/src/lib/activities.js +145 -0
  13. package/src/lib/activities.js.map +1 -0
  14. package/src/lib/activities.mjs +140 -0
  15. package/src/lib/activities.mjs.map +1 -0
  16. package/src/lib/attachments.d.mts +19 -0
  17. package/src/lib/attachments.d.ts +19 -0
  18. package/src/lib/attachments.js +93 -0
  19. package/src/lib/attachments.js.map +1 -0
  20. package/src/lib/attachments.mjs +89 -0
  21. package/src/lib/attachments.mjs.map +1 -0
  22. package/src/lib/chatter.d.mts +45 -0
  23. package/src/lib/chatter.d.ts +45 -0
  24. package/src/lib/chatter.js +222 -0
  25. package/src/lib/chatter.js.map +1 -0
  26. package/src/lib/chatter.mjs +217 -0
  27. package/src/lib/chatter.mjs.map +1 -0
  28. package/src/lib/followers.d.mts +15 -0
  29. package/src/lib/followers.d.ts +15 -0
  30. package/src/lib/followers.js +96 -0
  31. package/src/lib/followers.js.map +1 -0
  32. package/src/lib/followers.mjs +93 -0
  33. package/src/lib/followers.mjs.map +1 -0
  34. package/src/lib/labels.d.mts +43 -0
  35. package/src/lib/labels.d.ts +43 -0
  36. package/src/lib/labels.js +166 -0
  37. package/src/lib/labels.js.map +1 -0
  38. package/src/lib/labels.mjs +163 -0
  39. package/src/lib/labels.mjs.map +1 -0
  40. package/src/lib/memory.d.mts +22 -0
  41. package/src/lib/memory.d.ts +22 -0
  42. package/src/lib/memory.js +131 -0
  43. package/src/lib/memory.js.map +1 -0
  44. package/src/lib/memory.mjs +128 -0
  45. package/src/lib/memory.mjs.map +1 -0
  46. package/src/lib/mentions.d.mts +18 -0
  47. package/src/lib/mentions.d.ts +18 -0
  48. package/src/lib/mentions.js +94 -0
  49. package/src/lib/mentions.js.map +1 -0
  50. package/src/lib/mentions.mjs +90 -0
  51. package/src/lib/mentions.mjs.map +1 -0
  52. package/src/lib/reactions.d.mts +10 -0
  53. package/src/lib/reactions.d.ts +10 -0
  54. package/src/lib/reactions.js +53 -0
  55. package/src/lib/reactions.js.map +1 -0
  56. package/src/lib/reactions.mjs +49 -0
  57. package/src/lib/reactions.mjs.map +1 -0
  58. package/src/lib/slot.d.mts +17 -0
  59. package/src/lib/slot.d.ts +17 -0
  60. package/src/lib/slot.js +37 -0
  61. package/src/lib/slot.js.map +1 -0
  62. package/src/lib/slot.mjs +34 -0
  63. package/src/lib/slot.mjs.map +1 -0
  64. package/src/lib/source.d.mts +114 -0
  65. package/src/lib/source.d.ts +114 -0
  66. package/src/lib/source.js +9 -0
  67. package/src/lib/source.js.map +1 -0
  68. package/src/lib/source.mjs +8 -0
  69. package/src/lib/source.mjs.map +1 -0
  70. package/src/lib/styles.d.mts +4 -0
  71. package/src/lib/styles.d.ts +4 -0
  72. package/src/lib/styles.js +108 -0
  73. package/src/lib/styles.js.map +1 -0
  74. package/src/lib/styles.mjs +104 -0
  75. package/src/lib/styles.mjs.map +1 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fieldia-dev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @fieldia/chatter
2
+
3
+ A record's conversation and to-dos beside its [Fieldia](https://fieldia.dev)
4
+ form: messages to its followers, notes for the team, files, reactions,
5
+ replies, @mentions, activities and followers. Plain DOM, so it works the same
6
+ in plain JavaScript, React, Vue and Angular.
7
+
8
+ ```sh
9
+ npm install @fieldia/chatter
10
+ ```
11
+
12
+ ```ts
13
+ import { mountViewer } from '@fieldia/viewer';
14
+ import { chatterSlot } from '@fieldia/chatter';
15
+
16
+ mountViewer(host, { page, dataSource, recordId: 7, slots: { chatter: chatterSlot({ source }) } });
17
+ ```
18
+
19
+ A sheet whose `sidePanel` is the slot `chatter` shows it beside the record. It
20
+ waits while the record is new, starts once it is saved, and fetches again
21
+ after each save. On its own: `mountChatter(element, { source, record: { model, id } })`.
22
+
23
+ The chatter reaches your server only through a `source` you write for your
24
+ backend, as you write a data source. `messages` and `post` are all it needs;
25
+ each other method adds a part — `upload`, `react`, `people` (mentions,
26
+ assignees, followers), `activityTypes` / `activities` / `schedule` / `markDone`
27
+ / `cancel`, `followers` / `follow` / `unfollow` — and a part whose method is
28
+ missing is not shown. `createMemoryChatter` keeps everything in memory, for
29
+ demos and tests.
30
+
31
+ Message HTML is cleaned before it is shown; what people type is posted as
32
+ escaped text. MIT.
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@fieldia/chatter",
3
+ "version": "0.8.0",
4
+ "description": "A record's conversation and to-dos beside its Fieldia form: messages, notes, attachments, activities and followers, through an interface your app implements.",
5
+ "license": "MIT",
6
+ "homepage": "https://fieldia.dev",
7
+ "type": "commonjs",
8
+ "main": "./src/index.js",
9
+ "module": "./src/index.mjs",
10
+ "types": "./src/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": {
14
+ "import": "./src/index.d.mts",
15
+ "require": "./src/index.d.ts"
16
+ },
17
+ "import": "./src/index.mjs",
18
+ "require": "./src/index.js",
19
+ "default": "./src/index.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "dependencies": {
24
+ "@fieldia/core": "0.8.0",
25
+ "@fieldia/widgets": "0.8.0",
26
+ "tslib": "^2.8.1"
27
+ },
28
+ "sideEffects": false,
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/fieldia-dev/fieldia",
32
+ "directory": "libs/chatter"
33
+ },
34
+ "files": [
35
+ "src",
36
+ "README.md",
37
+ "LICENSE"
38
+ ]
39
+ }
@@ -0,0 +1,6 @@
1
+ export * from './lib/source.mjs';
2
+ export * from './lib/memory.mjs';
3
+ export * from './lib/labels.mjs';
4
+ export * from './lib/styles.mjs';
5
+ export * from './lib/chatter.mjs';
6
+ export * from './lib/slot.mjs';
package/src/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from './lib/source';
2
+ export * from './lib/memory';
3
+ export * from './lib/labels';
4
+ export * from './lib/styles';
5
+ export * from './lib/chatter';
6
+ export * from './lib/slot';
package/src/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/source"), exports);
5
+ tslib_1.__exportStar(require("./lib/memory"), exports);
6
+ tslib_1.__exportStar(require("./lib/labels"), exports);
7
+ tslib_1.__exportStar(require("./lib/styles"), exports);
8
+ tslib_1.__exportStar(require("./lib/chatter"), exports);
9
+ tslib_1.__exportStar(require("./lib/slot"), exports);
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/chatter/src/index.ts"],"names":[],"mappings":";;;AAAA,uDAA6B;AAC7B,uDAA6B;AAC7B,uDAA6B;AAC7B,uDAA6B;AAC7B,wDAA8B;AAC9B,qDAA2B"}
package/src/index.mjs ADDED
@@ -0,0 +1,7 @@
1
+ export * from './lib/source.mjs';
2
+ export * from './lib/memory.mjs';
3
+ export * from './lib/labels.mjs';
4
+ export * from './lib/styles.mjs';
5
+ export * from './lib/chatter.mjs';
6
+ export * from './lib/slot.mjs';
7
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../../libs/chatter/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { ChatterContext } from './chatter.mjs';
2
+ /** A day as `YYYY-MM-DD`, in the person's own time zone. */
3
+ export declare function isoDay(date: Date): string;
4
+ /** Whether an activity is late, due today, or ahead. */
5
+ export declare function dueState(due: string, today: string): 'overdue' | 'today' | 'planned';
6
+ /**
7
+ * The record's activities: what is to be done, by whom and by when, overdue
8
+ * first. Each can be marked done, with what came of it, or cancelled; a form
9
+ * schedules a new one. Every part the source cannot do is left out.
10
+ */
11
+ export declare function activitiesPart(context: ChatterContext, afterChange: () => Promise<void>): {
12
+ button: null;
13
+ element: null;
14
+ load: () => Promise<undefined>;
15
+ } | {
16
+ button: HTMLButtonElement | null;
17
+ element: HTMLElement;
18
+ load: () => Promise<void>;
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { ChatterContext } from './chatter';
2
+ /** A day as `YYYY-MM-DD`, in the person's own time zone. */
3
+ export declare function isoDay(date: Date): string;
4
+ /** Whether an activity is late, due today, or ahead. */
5
+ export declare function dueState(due: string, today: string): 'overdue' | 'today' | 'planned';
6
+ /**
7
+ * The record's activities: what is to be done, by whom and by when, overdue
8
+ * first. Each can be marked done, with what came of it, or cancelled; a form
9
+ * schedules a new one. Every part the source cannot do is left out.
10
+ */
11
+ export declare function activitiesPart(context: ChatterContext, afterChange: () => Promise<void>): {
12
+ button: null;
13
+ element: null;
14
+ load: () => Promise<undefined>;
15
+ } | {
16
+ button: HTMLButtonElement | null;
17
+ element: HTMLElement;
18
+ load: () => Promise<void>;
19
+ };
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isoDay = isoDay;
4
+ exports.dueState = dueState;
5
+ exports.activitiesPart = activitiesPart;
6
+ const core_1 = require("@fieldia/core");
7
+ const widgets_1 = require("@fieldia/widgets");
8
+ /** A day as `YYYY-MM-DD`, in the person's own time zone. */
9
+ function isoDay(date) {
10
+ const pad = (n) => String(n).padStart(2, '0');
11
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
12
+ }
13
+ /** Days after `day`, as `YYYY-MM-DD`. */
14
+ function daysAfter(day, days) {
15
+ const next = new Date(day);
16
+ next.setDate(next.getDate() + days);
17
+ return isoDay(next);
18
+ }
19
+ /** Whether an activity is late, due today, or ahead. */
20
+ function dueState(due, today) {
21
+ return due < today ? 'overdue' : due === today ? 'today' : 'planned';
22
+ }
23
+ /**
24
+ * The record's activities: what is to be done, by whom and by when, overdue
25
+ * first. Each can be marked done, with what came of it, or cancelled; a form
26
+ * schedules a new one. Every part the source cannot do is left out.
27
+ */
28
+ function activitiesPart(context, afterChange) {
29
+ const { el, labels, source, doc, icons, locale } = context;
30
+ if (!source.activities)
31
+ return { button: null, element: null, load: async () => undefined };
32
+ const list = el('ul', { class: 'fd-activity-list' });
33
+ const element = el('section', { class: 'fd-activities', 'aria-label': labels.activities }, list);
34
+ const dayFormat = new Intl.DateTimeFormat(locale === 'ar' ? 'ar-EG-u-nu-latn' : locale, { dateStyle: 'medium' });
35
+ const canSchedule = !!(source.schedule && source.activityTypes && source.people);
36
+ // ---- the list ----------------------------------------------------------------
37
+ function item(activity, today) {
38
+ const when = dueState(activity.due, today);
39
+ const icon = (0, widgets_1.drawIcon)(doc, activity.type.icon ?? 'clock', icons);
40
+ const dueText = when === 'overdue' ? labels.overdue : when === 'today' ? labels.today : (0, core_1.fill)(labels.dueOn, { date: dayFormat.format(new Date(`${activity.due}T12:00:00`)) });
41
+ const row = el('li', { class: 'fd-activity', 'data-when': when }, el('span', { class: 'fd-activity-icon' }, ...(icon ? [icon] : [])), el('div', { class: 'fd-activity-content' }, el('span', { class: 'fd-activity-summary' }, activity.summary || activity.type.name), el('span', { class: 'fd-activity-meta' }, el('span', { class: 'fd-activity-due' }, dueText), ' · ', activity.assignee.name)));
42
+ const actions = el('div', { class: 'fd-activity-actions' });
43
+ if (source.markDone) {
44
+ const done = el('button', { type: 'button', class: 'fd-button fd-button-link' }, labels.markDone);
45
+ const feedback = el('textarea', { class: 'fd-input', rows: '2', placeholder: labels.whatHappened, 'aria-label': labels.whatHappened });
46
+ const confirm = el('button', { type: 'button', class: 'fd-button fd-button-primary' }, labels.done);
47
+ const finishing = el('div', { class: 'fd-activity-finish', hidden: '' }, feedback, confirm);
48
+ done.addEventListener('click', () => {
49
+ finishing.hidden = !finishing.hidden;
50
+ if (!finishing.hidden)
51
+ feedback.focus();
52
+ });
53
+ confirm.addEventListener('click', async () => {
54
+ const record = context.record();
55
+ if (!record || !source.markDone)
56
+ return;
57
+ await source.markDone(record, activity.id, feedback.value.trim() || undefined);
58
+ await load();
59
+ await afterChange();
60
+ });
61
+ actions.append(done);
62
+ row.append(actions, finishing);
63
+ }
64
+ if (source.cancel) {
65
+ const cancel = el('button', { type: 'button', class: 'fd-button fd-button-link' }, labels.cancelActivity);
66
+ cancel.addEventListener('click', async () => {
67
+ const record = context.record();
68
+ if (!record || !source.cancel)
69
+ return;
70
+ await source.cancel(record, activity.id);
71
+ await load();
72
+ });
73
+ actions.append(cancel);
74
+ if (!row.contains(actions))
75
+ row.append(actions);
76
+ }
77
+ return row;
78
+ }
79
+ async function load() {
80
+ const record = context.record();
81
+ if (!record || !source.activities) {
82
+ element.hidden = true;
83
+ return;
84
+ }
85
+ const activities = [...(await source.activities(record))].sort((a, b) => a.due.localeCompare(b.due));
86
+ if (context.record() !== record)
87
+ return;
88
+ const today = isoDay(context.now());
89
+ list.replaceChildren(...activities.map((activity) => item(activity, today)));
90
+ element.hidden = !activities.length && form.hidden;
91
+ }
92
+ // ---- scheduling --------------------------------------------------------------
93
+ const type = el('select', { class: 'fd-input', name: 'type', 'aria-label': labels.activityType });
94
+ const summary = el('input', { class: 'fd-input', name: 'summary', 'aria-label': labels.summary, placeholder: labels.summary });
95
+ const due = el('input', { class: 'fd-input', type: 'date', name: 'due', 'aria-label': labels.dueDate });
96
+ const assignee = el('select', { class: 'fd-input', name: 'assignee', 'aria-label': labels.assignedTo });
97
+ const schedule = el('button', { type: 'button', class: 'fd-button fd-button-primary' }, labels.schedule);
98
+ const cancelForm = el('button', { type: 'button', class: 'fd-button' }, labels.cancel);
99
+ const label = (text, control) => el('label', { class: 'fd-activity-field' }, el('span', {}, text), control);
100
+ const form = el('div', { class: 'fd-activity-form', hidden: '' }, label(labels.activityType, type), label(labels.summary, summary), label(labels.dueDate, due), label(labels.assignedTo, assignee), el('div', { class: 'fd-composer-actions' }, schedule, cancelForm));
101
+ let types = [];
102
+ let people = [];
103
+ /** The due date follows the type until someone chooses one. */
104
+ let dueChosen = false;
105
+ const dueFromType = () => {
106
+ if (dueChosen)
107
+ return;
108
+ const chosen = types.find((t) => String(t.id) === type.value);
109
+ due.value = daysAfter(context.now(), chosen?.daysUntilDue ?? 0);
110
+ };
111
+ type.addEventListener('change', dueFromType);
112
+ due.addEventListener('input', () => (dueChosen = true));
113
+ due.addEventListener('change', () => (dueChosen = true));
114
+ const button = canSchedule ? el('button', { type: 'button', class: 'fd-button' }, labels.scheduleActivity) : null;
115
+ button?.addEventListener('click', async () => {
116
+ if (!source.activityTypes || !source.people)
117
+ return;
118
+ [types, people] = await Promise.all([source.activityTypes(), source.people('')]);
119
+ type.replaceChildren(...types.map((t) => el('option', { value: String(t.id) }, t.name)));
120
+ assignee.replaceChildren(...people.map((p) => el('option', { value: String(p.id) }, p.name)));
121
+ summary.value = '';
122
+ dueChosen = false;
123
+ dueFromType();
124
+ form.hidden = false;
125
+ element.hidden = false;
126
+ type.focus();
127
+ });
128
+ cancelForm.addEventListener('click', () => {
129
+ form.hidden = true;
130
+ void load();
131
+ });
132
+ schedule.addEventListener('click', async () => {
133
+ const record = context.record();
134
+ const chosenType = types.find((t) => String(t.id) === type.value);
135
+ const person = people.find((p) => String(p.id) === assignee.value);
136
+ if (!record || !source.schedule || !chosenType || !person || !due.value)
137
+ return;
138
+ await source.schedule(record, { typeId: chosenType.id, summary: summary.value.trim() || undefined, due: due.value, assigneeId: person.id });
139
+ form.hidden = true;
140
+ await load();
141
+ });
142
+ element.prepend(form);
143
+ return { button, element, load };
144
+ }
145
+ //# sourceMappingURL=activities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activities.js","sourceRoot":"","sources":["../../../../../libs/chatter/src/lib/activities.ts"],"names":[],"mappings":";;AAMA,wBAGC;AAUD,4BAEC;AAOD,wCAoIC;AAhKD,wCAAqC;AACrC,8CAA4C;AAI5C,4DAA4D;AAC5D,SAAgB,MAAM,CAAC,IAAU;IAC/B,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtD,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;AACpF,CAAC;AAED,yCAAyC;AACzC,SAAS,SAAS,CAAC,GAAS,EAAE,IAAY;IACxC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,wDAAwD;AACxD,SAAgB,QAAQ,CAAC,GAAW,EAAE,KAAa;IACjD,OAAO,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAAC,OAAuB,EAAE,WAAgC;IACtF,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3D,IAAI,CAAC,MAAM,CAAC,UAAU;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;IAC5F,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,EAAE,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC;IACjG,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjH,MAAM,WAAW,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;IAEjF,iFAAiF;IAEjF,SAAS,IAAI,CAAC,QAAkB,EAAE,KAAa;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,IAAA,kBAAQ,EAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,EAAE,KAAK,CAAC,CAAC;QACjE,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAA,WAAI,EAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7K,MAAM,GAAG,GAAG,EAAE,CACZ,IAAI,EACJ,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,EAAE,EAC3C,EAAE,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAClE,EAAE,CACA,KAAK,EACL,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAChC,EAAE,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EACpF,EAAE,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAC5H,CACF,CAAC;QACF,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAC5D,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,0BAA0B,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClG,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YACvI,MAAM,OAAO,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,6BAA6B,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACpG,MAAM,SAAS,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC5F,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;gBAClC,SAAS,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC;gBACrC,IAAI,CAAC,SAAS,CAAC,MAAM;oBAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;YAC1C,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;gBAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;gBAChC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ;oBAAE,OAAO;gBACxC,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC,CAAC;gBAC/E,MAAM,IAAI,EAAE,CAAC;gBACb,MAAM,WAAW,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACrB,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,0BAA0B,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;YAC1G,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;gBAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;gBAChC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM;oBAAE,OAAO;gBACtC,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;gBACzC,MAAM,IAAI,EAAE,CAAC;YACf,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,UAAU,IAAI;QACjB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAClC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YACtB,OAAO;QACT,CAAC;QACD,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACrG,IAAI,OAAO,CAAC,MAAM,EAAE,KAAK,MAAM;YAAE,OAAO;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,eAAe,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7E,OAAO,CAAC,MAAM,GAAG,CAAC,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;IACrD,CAAC;IAED,iFAAiF;IAEjF,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IAClG,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/H,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACxG,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IACxG,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,6BAA6B,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzG,MAAM,UAAU,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,OAAoB,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACjI,MAAM,IAAI,GAAG,EAAE,CACb,KAAK,EACL,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,EAAE,EAAE,EACzC,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,EAChC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,EAC9B,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,EAC1B,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,EAClC,EAAE,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,CAClE,CAAC;IACF,IAAI,KAAK,GAAmB,EAAE,CAAC;IAC/B,IAAI,MAAM,GAAa,EAAE,CAAC;IAC1B,+DAA+D;IAC/D,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,WAAW,GAAG,GAAG,EAAE;QACvB,IAAI,SAAS;YAAE,OAAO;QACtB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9D,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,YAAY,IAAI,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC;IACF,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC7C,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;IACxD,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;IAEzD,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClH,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAC3C,IAAI,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO;QACpD,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACjF,IAAI,CAAC,eAAe,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzF,QAAQ,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9F,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;QACnB,SAAS,GAAG,KAAK,CAAC;QAClB,WAAW,EAAE,CAAC;QACd,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IACH,UAAU,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QACxC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,KAAK,IAAI,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;IACH,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK;YAAE,OAAO;QAChF,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5I,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACnC,CAAC"}
@@ -0,0 +1,140 @@
1
+ import { fill } from '@fieldia/core';
2
+ import { drawIcon } from '@fieldia/widgets';
3
+ /** A day as `YYYY-MM-DD`, in the person's own time zone. */
4
+ export function isoDay(date) {
5
+ const pad = (n) => String(n).padStart(2, '0');
6
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
7
+ }
8
+ /** Days after `day`, as `YYYY-MM-DD`. */
9
+ function daysAfter(day, days) {
10
+ const next = new Date(day);
11
+ next.setDate(next.getDate() + days);
12
+ return isoDay(next);
13
+ }
14
+ /** Whether an activity is late, due today, or ahead. */
15
+ export function dueState(due, today) {
16
+ return due < today ? 'overdue' : due === today ? 'today' : 'planned';
17
+ }
18
+ /**
19
+ * The record's activities: what is to be done, by whom and by when, overdue
20
+ * first. Each can be marked done, with what came of it, or cancelled; a form
21
+ * schedules a new one. Every part the source cannot do is left out.
22
+ */
23
+ export function activitiesPart(context, afterChange) {
24
+ const { el, labels, source, doc, icons, locale } = context;
25
+ if (!source.activities)
26
+ return { button: null, element: null, load: async () => undefined };
27
+ const list = el('ul', { class: 'fd-activity-list' });
28
+ const element = el('section', { class: 'fd-activities', 'aria-label': labels.activities }, list);
29
+ const dayFormat = new Intl.DateTimeFormat(locale === 'ar' ? 'ar-EG-u-nu-latn' : locale, { dateStyle: 'medium' });
30
+ const canSchedule = !!(source.schedule && source.activityTypes && source.people);
31
+ // ---- the list ----------------------------------------------------------------
32
+ function item(activity, today) {
33
+ const when = dueState(activity.due, today);
34
+ const icon = drawIcon(doc, activity.type.icon ?? 'clock', icons);
35
+ const dueText = when === 'overdue' ? labels.overdue : when === 'today' ? labels.today : fill(labels.dueOn, { date: dayFormat.format(new Date(`${activity.due}T12:00:00`)) });
36
+ const row = el('li', { class: 'fd-activity', 'data-when': when }, el('span', { class: 'fd-activity-icon' }, ...(icon ? [icon] : [])), el('div', { class: 'fd-activity-content' }, el('span', { class: 'fd-activity-summary' }, activity.summary || activity.type.name), el('span', { class: 'fd-activity-meta' }, el('span', { class: 'fd-activity-due' }, dueText), ' · ', activity.assignee.name)));
37
+ const actions = el('div', { class: 'fd-activity-actions' });
38
+ if (source.markDone) {
39
+ const done = el('button', { type: 'button', class: 'fd-button fd-button-link' }, labels.markDone);
40
+ const feedback = el('textarea', { class: 'fd-input', rows: '2', placeholder: labels.whatHappened, 'aria-label': labels.whatHappened });
41
+ const confirm = el('button', { type: 'button', class: 'fd-button fd-button-primary' }, labels.done);
42
+ const finishing = el('div', { class: 'fd-activity-finish', hidden: '' }, feedback, confirm);
43
+ done.addEventListener('click', () => {
44
+ finishing.hidden = !finishing.hidden;
45
+ if (!finishing.hidden)
46
+ feedback.focus();
47
+ });
48
+ confirm.addEventListener('click', async () => {
49
+ const record = context.record();
50
+ if (!record || !source.markDone)
51
+ return;
52
+ await source.markDone(record, activity.id, feedback.value.trim() || undefined);
53
+ await load();
54
+ await afterChange();
55
+ });
56
+ actions.append(done);
57
+ row.append(actions, finishing);
58
+ }
59
+ if (source.cancel) {
60
+ const cancel = el('button', { type: 'button', class: 'fd-button fd-button-link' }, labels.cancelActivity);
61
+ cancel.addEventListener('click', async () => {
62
+ const record = context.record();
63
+ if (!record || !source.cancel)
64
+ return;
65
+ await source.cancel(record, activity.id);
66
+ await load();
67
+ });
68
+ actions.append(cancel);
69
+ if (!row.contains(actions))
70
+ row.append(actions);
71
+ }
72
+ return row;
73
+ }
74
+ async function load() {
75
+ const record = context.record();
76
+ if (!record || !source.activities) {
77
+ element.hidden = true;
78
+ return;
79
+ }
80
+ const activities = [...(await source.activities(record))].sort((a, b) => a.due.localeCompare(b.due));
81
+ if (context.record() !== record)
82
+ return;
83
+ const today = isoDay(context.now());
84
+ list.replaceChildren(...activities.map((activity) => item(activity, today)));
85
+ element.hidden = !activities.length && form.hidden;
86
+ }
87
+ // ---- scheduling --------------------------------------------------------------
88
+ const type = el('select', { class: 'fd-input', name: 'type', 'aria-label': labels.activityType });
89
+ const summary = el('input', { class: 'fd-input', name: 'summary', 'aria-label': labels.summary, placeholder: labels.summary });
90
+ const due = el('input', { class: 'fd-input', type: 'date', name: 'due', 'aria-label': labels.dueDate });
91
+ const assignee = el('select', { class: 'fd-input', name: 'assignee', 'aria-label': labels.assignedTo });
92
+ const schedule = el('button', { type: 'button', class: 'fd-button fd-button-primary' }, labels.schedule);
93
+ const cancelForm = el('button', { type: 'button', class: 'fd-button' }, labels.cancel);
94
+ const label = (text, control) => el('label', { class: 'fd-activity-field' }, el('span', {}, text), control);
95
+ const form = el('div', { class: 'fd-activity-form', hidden: '' }, label(labels.activityType, type), label(labels.summary, summary), label(labels.dueDate, due), label(labels.assignedTo, assignee), el('div', { class: 'fd-composer-actions' }, schedule, cancelForm));
96
+ let types = [];
97
+ let people = [];
98
+ /** The due date follows the type until someone chooses one. */
99
+ let dueChosen = false;
100
+ const dueFromType = () => {
101
+ if (dueChosen)
102
+ return;
103
+ const chosen = types.find((t) => String(t.id) === type.value);
104
+ due.value = daysAfter(context.now(), chosen?.daysUntilDue ?? 0);
105
+ };
106
+ type.addEventListener('change', dueFromType);
107
+ due.addEventListener('input', () => (dueChosen = true));
108
+ due.addEventListener('change', () => (dueChosen = true));
109
+ const button = canSchedule ? el('button', { type: 'button', class: 'fd-button' }, labels.scheduleActivity) : null;
110
+ button?.addEventListener('click', async () => {
111
+ if (!source.activityTypes || !source.people)
112
+ return;
113
+ [types, people] = await Promise.all([source.activityTypes(), source.people('')]);
114
+ type.replaceChildren(...types.map((t) => el('option', { value: String(t.id) }, t.name)));
115
+ assignee.replaceChildren(...people.map((p) => el('option', { value: String(p.id) }, p.name)));
116
+ summary.value = '';
117
+ dueChosen = false;
118
+ dueFromType();
119
+ form.hidden = false;
120
+ element.hidden = false;
121
+ type.focus();
122
+ });
123
+ cancelForm.addEventListener('click', () => {
124
+ form.hidden = true;
125
+ void load();
126
+ });
127
+ schedule.addEventListener('click', async () => {
128
+ const record = context.record();
129
+ const chosenType = types.find((t) => String(t.id) === type.value);
130
+ const person = people.find((p) => String(p.id) === assignee.value);
131
+ if (!record || !source.schedule || !chosenType || !person || !due.value)
132
+ return;
133
+ await source.schedule(record, { typeId: chosenType.id, summary: summary.value.trim() || undefined, due: due.value, assigneeId: person.id });
134
+ form.hidden = true;
135
+ await load();
136
+ });
137
+ element.prepend(form);
138
+ return { button, element, load };
139
+ }
140
+ //# sourceMappingURL=activities.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activities.mjs","sourceRoot":"","sources":["../../../../../libs/chatter/src/lib/activities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ChatterContext } from './chatter.mjs';
2
+ import type { Attachment } from './source.mjs';
3
+ /** A message's files: an image as a preview that opens it, any other file as a link with its size. */
4
+ export declare function attachmentList(context: ChatterContext, attachments: readonly Attachment[], remove?: (attachment: Attachment) => void): HTMLElement;
5
+ /**
6
+ * The composer's files: a button to choose them, dropped ones too, each
7
+ * stored at once through the source and shown until it is sent or taken away.
8
+ * Without a source that stores files, there is nothing to show.
9
+ */
10
+ export declare function composerFiles(context: ChatterContext, onProblem: (message: string) => void): {
11
+ /** The button, or nothing when the source cannot store files. */
12
+ element: HTMLDivElement | null;
13
+ chips: HTMLDivElement;
14
+ files: () => Attachment[];
15
+ busy: () => boolean;
16
+ clear(): void;
17
+ /** Files dropped on the composer are taken too. */
18
+ takeDropsOn(target: HTMLElement): void;
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { ChatterContext } from './chatter';
2
+ import type { Attachment } from './source';
3
+ /** A message's files: an image as a preview that opens it, any other file as a link with its size. */
4
+ export declare function attachmentList(context: ChatterContext, attachments: readonly Attachment[], remove?: (attachment: Attachment) => void): HTMLElement;
5
+ /**
6
+ * The composer's files: a button to choose them, dropped ones too, each
7
+ * stored at once through the source and shown until it is sent or taken away.
8
+ * Without a source that stores files, there is nothing to show.
9
+ */
10
+ export declare function composerFiles(context: ChatterContext, onProblem: (message: string) => void): {
11
+ /** The button, or nothing when the source cannot store files. */
12
+ element: HTMLDivElement | null;
13
+ chips: HTMLDivElement;
14
+ files: () => Attachment[];
15
+ busy: () => boolean;
16
+ clear(): void;
17
+ /** Files dropped on the composer are taken too. */
18
+ takeDropsOn(target: HTMLElement): void;
19
+ };
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.attachmentList = attachmentList;
4
+ exports.composerFiles = composerFiles;
5
+ const core_1 = require("@fieldia/core");
6
+ const widgets_1 = require("@fieldia/widgets");
7
+ const isImage = (attachment) => attachment.type.startsWith('image/') && !!attachment.url;
8
+ /** A message's files: an image as a preview that opens it, any other file as a link with its size. */
9
+ function attachmentList(context, attachments, remove) {
10
+ const { el, doc, labels, icons } = context;
11
+ return el('ul', { class: 'fd-attachments' }, ...attachments.map((attachment) => {
12
+ const item = el('li', { class: 'fd-attachment' });
13
+ if (isImage(attachment)) {
14
+ const preview = el('img', { src: attachment.url, alt: attachment.name });
15
+ item.append(el('a', { class: 'fd-attachment-image', href: attachment.url, target: '_blank', rel: 'noopener' }, preview));
16
+ }
17
+ else {
18
+ const icon = (0, widgets_1.drawIcon)(doc, 'file', icons);
19
+ const words = el('span', { class: 'fd-attachment-words' }, el('span', { class: 'fd-attachment-name' }, attachment.name), el('span', { class: 'fd-attachment-size' }, (0, core_1.formatBytes)(attachment.size)));
20
+ const parts = [...(icon ? [icon] : []), words];
21
+ item.append(attachment.url
22
+ ? el('a', { class: 'fd-attachment-file', href: attachment.url, target: '_blank', rel: 'noopener', download: attachment.name }, ...parts)
23
+ : el('span', { class: 'fd-attachment-file' }, ...parts));
24
+ }
25
+ if (remove) {
26
+ const drop = el('button', { type: 'button', class: 'fd-attachment-remove', 'aria-label': (0, core_1.fill)(labels.removeAttachment, { name: attachment.name }) }, '×');
27
+ drop.addEventListener('click', () => remove(attachment));
28
+ item.append(drop);
29
+ }
30
+ return item;
31
+ }));
32
+ }
33
+ /**
34
+ * The composer's files: a button to choose them, dropped ones too, each
35
+ * stored at once through the source and shown until it is sent or taken away.
36
+ * Without a source that stores files, there is nothing to show.
37
+ */
38
+ function composerFiles(context, onProblem) {
39
+ const { el, labels, source, doc, icons } = context;
40
+ let files = [];
41
+ let uploading = 0;
42
+ const input = el('input', { type: 'file', multiple: '', hidden: '' });
43
+ const icon = (0, widgets_1.drawIcon)(doc, 'link', icons);
44
+ const button = el('button', { type: 'button', class: 'fd-button fd-attach' }, ...(icon ? [icon] : []), labels.attach);
45
+ const chips = el('div', { class: 'fd-composer-files' });
46
+ const element = el('div', { class: 'fd-composer-attach' }, button, input);
47
+ const draw = () => chips.replaceChildren(...(files.length ? [attachmentList(context, files, (gone) => ((files = files.filter((f) => f !== gone)), draw()))] : []));
48
+ async function take(chosen) {
49
+ const record = context.record();
50
+ if (!source.upload || !record)
51
+ return;
52
+ uploading += chosen.length;
53
+ for (const file of chosen) {
54
+ try {
55
+ files = [...files, await source.upload(record, file)];
56
+ draw();
57
+ }
58
+ catch (error) {
59
+ onProblem((0, core_1.fill)(labels.couldNotSend, { reason: error.message }));
60
+ }
61
+ finally {
62
+ uploading--;
63
+ }
64
+ }
65
+ }
66
+ button.addEventListener('click', () => input.click());
67
+ input.addEventListener('change', () => {
68
+ void take([...(input.files ?? [])]);
69
+ input.value = '';
70
+ });
71
+ return {
72
+ /** The button, or nothing when the source cannot store files. */
73
+ element: source.upload ? element : null,
74
+ chips,
75
+ files: () => files,
76
+ busy: () => uploading > 0,
77
+ clear() {
78
+ files = [];
79
+ draw();
80
+ },
81
+ /** Files dropped on the composer are taken too. */
82
+ takeDropsOn(target) {
83
+ if (!source.upload)
84
+ return;
85
+ target.addEventListener('dragover', (event) => event.preventDefault());
86
+ target.addEventListener('drop', (event) => {
87
+ event.preventDefault();
88
+ void take([...(event.dataTransfer?.files ?? [])]);
89
+ });
90
+ },
91
+ };
92
+ }
93
+ //# sourceMappingURL=attachments.js.map