profile-pane 3.2.1 → 3.2.2-test.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 (241) hide show
  1. package/README.md +50 -0
  2. package/lib/303.profile-pane.js +1362 -0
  3. package/lib/303.profile-pane.js.map +1 -0
  4. package/lib/303.profile-pane.min.js +2 -0
  5. package/lib/303.profile-pane.min.js.map +1 -0
  6. package/lib/ProfileView.css +1090 -0
  7. package/lib/ProfileView.d.ts +2 -1
  8. package/lib/ProfileView.d.ts.map +1 -1
  9. package/lib/ProfileView.js +64 -36
  10. package/lib/buttonsHelper.d.ts +1 -1
  11. package/lib/buttonsHelper.d.ts.map +1 -1
  12. package/lib/buttonsHelper.js +2 -1
  13. package/lib/editProfilePane/EditCVCard.js +1 -1
  14. package/lib/editProfilePane/EditCommunitiesCard.js +1 -1
  15. package/lib/editProfilePane/EditFriendsCard.js +1 -1
  16. package/lib/editProfilePane/EditProfileView.d.ts +1 -1
  17. package/lib/editProfilePane/EditProfileView.d.ts.map +1 -1
  18. package/lib/editProfilePane/EditProfileView.js +4 -5
  19. package/lib/editProfilePane/editProfilePresenter.d.ts.map +1 -1
  20. package/lib/editProfilePane/editProfilePresenter.js +5 -4
  21. package/lib/icons-svg/profileIcons.d.ts +1 -1
  22. package/lib/icons-svg/profileIcons.d.ts.map +1 -1
  23. package/lib/icons-svg/profileIcons.js +9 -17
  24. package/lib/index.d.ts +1 -8
  25. package/lib/index.d.ts.map +1 -1
  26. package/lib/index.js +74 -40
  27. package/lib/ontology/otherPreferencesForm.ttl +32 -0
  28. package/lib/ontology/resumeForm.ttl +349 -0
  29. package/lib/ontology/socialMedia.ttl +433 -0
  30. package/lib/profile-pane.js +32266 -13247
  31. package/lib/profile-pane.js.map +1 -1
  32. package/lib/profile-pane.min.js +2315 -935
  33. package/lib/profile-pane.min.js.map +1 -1
  34. package/lib/rdfFormsHelper.d.ts +13 -1
  35. package/lib/rdfFormsHelper.d.ts.map +1 -1
  36. package/lib/rdfFormsHelper.js +13 -1
  37. package/lib/sections/bio/BioEditDialog.d.ts.map +1 -1
  38. package/lib/sections/bio/BioEditDialog.js +7 -7
  39. package/lib/sections/bio/BioSection.css +300 -0
  40. package/lib/sections/bio/BioSection.d.ts +3 -2
  41. package/lib/sections/bio/BioSection.d.ts.map +1 -1
  42. package/lib/sections/bio/BioSection.js +26 -19
  43. package/lib/sections/bio/mutations.d.ts.map +1 -1
  44. package/lib/sections/bio/mutations.js +14 -3
  45. package/lib/sections/contactInfo/ContactInfoEditDialog.css +354 -0
  46. package/lib/sections/contactInfo/ContactInfoEditDialog.d.ts +3 -1
  47. package/lib/sections/contactInfo/ContactInfoEditDialog.d.ts.map +1 -1
  48. package/lib/sections/contactInfo/ContactInfoEditDialog.js +183 -98
  49. package/lib/sections/contactInfo/ContactInfoSection.css +125 -0
  50. package/lib/sections/contactInfo/ContactInfoSection.d.ts +2 -0
  51. package/lib/sections/contactInfo/ContactInfoSection.d.ts.map +1 -1
  52. package/lib/sections/contactInfo/ContactInfoSection.js +64 -41
  53. package/lib/sections/contactInfo/mutations.d.ts.map +1 -1
  54. package/lib/sections/contactInfo/mutations.js +51 -16
  55. package/lib/sections/education/EducationEditDialog.d.ts +3 -1
  56. package/lib/sections/education/EducationEditDialog.d.ts.map +1 -1
  57. package/lib/sections/education/EducationEditDialog.js +170 -92
  58. package/lib/sections/education/EducationSection.css +133 -0
  59. package/lib/sections/education/EducationSection.d.ts +3 -2
  60. package/lib/sections/education/EducationSection.d.ts.map +1 -1
  61. package/lib/sections/education/EducationSection.js +32 -25
  62. package/lib/sections/education/mutations.d.ts.map +1 -1
  63. package/lib/sections/education/mutations.js +14 -3
  64. package/lib/sections/heading/HeadingEditDialog.d.ts +4 -1
  65. package/lib/sections/heading/HeadingEditDialog.d.ts.map +1 -1
  66. package/lib/sections/heading/HeadingEditDialog.js +287 -162
  67. package/lib/sections/heading/HeadingSection.css +862 -0
  68. package/lib/sections/heading/HeadingSection.d.ts +3 -2
  69. package/lib/sections/heading/HeadingSection.d.ts.map +1 -1
  70. package/lib/sections/heading/HeadingSection.js +63 -32
  71. package/lib/sections/heading/imageHelpers.d.ts +1 -0
  72. package/lib/sections/heading/imageHelpers.d.ts.map +1 -1
  73. package/lib/sections/heading/imageHelpers.js +40 -1
  74. package/lib/sections/heading/mutations.d.ts.map +1 -1
  75. package/lib/sections/heading/mutations.js +86 -23
  76. package/lib/sections/heading/selectors.d.ts.map +1 -1
  77. package/lib/sections/heading/selectors.js +14 -3
  78. package/lib/sections/heading/types.d.ts +1 -2
  79. package/lib/sections/heading/types.d.ts.map +1 -1
  80. package/lib/sections/languages/LanguageEditDialog.d.ts +3 -1
  81. package/lib/sections/languages/LanguageEditDialog.d.ts.map +1 -1
  82. package/lib/sections/languages/LanguageEditDialog.js +202 -119
  83. package/lib/sections/languages/LanguageSection.css +53 -0
  84. package/lib/sections/languages/LanguageSection.d.ts +2 -0
  85. package/lib/sections/languages/LanguageSection.d.ts.map +1 -1
  86. package/lib/sections/languages/LanguageSection.js +42 -31
  87. package/lib/sections/languages/mutations.d.ts.map +1 -1
  88. package/lib/sections/languages/mutations.js +60 -161
  89. package/lib/sections/languages/selectors.d.ts.map +1 -1
  90. package/lib/sections/languages/selectors.js +1 -2
  91. package/lib/sections/projects/ProjectEditDialog.d.ts +2 -1
  92. package/lib/sections/projects/ProjectEditDialog.d.ts.map +1 -1
  93. package/lib/sections/projects/ProjectEditDialog.js +13 -24
  94. package/lib/sections/projects/ProjectSection.css +368 -0
  95. package/lib/sections/projects/ProjectSection.d.ts +2 -1
  96. package/lib/sections/projects/ProjectSection.d.ts.map +1 -1
  97. package/lib/sections/projects/ProjectSection.js +116 -34
  98. package/lib/sections/projects/mutations.d.ts.map +1 -1
  99. package/lib/sections/projects/mutations.js +109 -132
  100. package/lib/sections/projects/selectors.d.ts.map +1 -1
  101. package/lib/sections/projects/selectors.js +4 -45
  102. package/lib/{QRCodeCard.d.ts → sections/qrcode/QRCodeCard.d.ts} +2 -1
  103. package/lib/sections/qrcode/QRCodeCard.d.ts.map +1 -0
  104. package/lib/{QRCodeCard.js → sections/qrcode/QRCodeCard.js} +59 -11
  105. package/lib/sections/qrcode/QRCodeSection.css +108 -0
  106. package/lib/sections/qrcode/QRCodeSection.d.ts +4 -0
  107. package/lib/sections/qrcode/QRCodeSection.d.ts.map +1 -0
  108. package/lib/sections/qrcode/QRCodeSection.js +17 -0
  109. package/lib/sections/resume/ResumeEditDialog.d.ts +10 -1
  110. package/lib/sections/resume/ResumeEditDialog.d.ts.map +1 -1
  111. package/lib/sections/resume/ResumeEditDialog.js +531 -149
  112. package/lib/sections/resume/ResumeSection.css +350 -0
  113. package/lib/sections/resume/ResumeSection.d.ts +3 -2
  114. package/lib/sections/resume/ResumeSection.d.ts.map +1 -1
  115. package/lib/sections/resume/ResumeSection.js +78 -49
  116. package/lib/sections/resume/mutations.d.ts.map +1 -1
  117. package/lib/sections/resume/mutations.js +17 -3
  118. package/lib/sections/resume/selectors.d.ts.map +1 -1
  119. package/lib/sections/resume/selectors.js +1 -0
  120. package/lib/sections/resume/types.d.ts +1 -0
  121. package/lib/sections/resume/types.d.ts.map +1 -1
  122. package/lib/sections/shared/collapsibleSection.d.ts.map +1 -1
  123. package/lib/sections/shared/collapsibleSection.js +1 -0
  124. package/lib/sections/shared/phoneCountries.d.ts +1 -1
  125. package/lib/sections/shared/phoneCountries.d.ts.map +1 -1
  126. package/lib/sections/shared/phoneCountries.js +2 -2
  127. package/lib/sections/shared/projectCommunityNodes.d.ts +6 -0
  128. package/lib/sections/shared/projectCommunityNodes.d.ts.map +1 -0
  129. package/lib/sections/shared/projectCommunityNodes.js +56 -0
  130. package/lib/sections/shared/rdfMutationHelpers.d.ts +35 -2
  131. package/lib/sections/shared/rdfMutationHelpers.d.ts.map +1 -1
  132. package/lib/sections/shared/rdfMutationHelpers.js +290 -14
  133. package/lib/sections/shared/sectionCardHelpers.d.ts.map +1 -1
  134. package/lib/sections/shared/sectionCardHelpers.js +80 -11
  135. package/lib/sections/shared/types.d.ts +24 -0
  136. package/lib/sections/shared/types.d.ts.map +1 -1
  137. package/lib/sections/skills/SkillsEditDialog.d.ts +3 -1
  138. package/lib/sections/skills/SkillsEditDialog.d.ts.map +1 -1
  139. package/lib/sections/skills/SkillsEditDialog.js +136 -115
  140. package/lib/sections/skills/SkillsSection.css +173 -0
  141. package/lib/sections/skills/SkillsSection.d.ts +2 -0
  142. package/lib/sections/skills/SkillsSection.d.ts.map +1 -1
  143. package/lib/sections/skills/SkillsSection.js +107 -47
  144. package/lib/sections/skills/mutations.d.ts.map +1 -1
  145. package/lib/sections/skills/mutations.js +25 -21
  146. package/lib/sections/skills/selectors.d.ts.map +1 -1
  147. package/lib/sections/skills/selectors.js +5 -3
  148. package/lib/sections/social/SocialEditDialog.d.ts +3 -1
  149. package/lib/sections/social/SocialEditDialog.d.ts.map +1 -1
  150. package/lib/sections/social/SocialEditDialog.js +170 -62
  151. package/lib/sections/social/SocialSection.css +194 -0
  152. package/lib/sections/social/SocialSection.d.ts +4 -3
  153. package/lib/sections/social/SocialSection.d.ts.map +1 -1
  154. package/lib/sections/social/SocialSection.js +59 -43
  155. package/lib/sections/social/mutations.d.ts.map +1 -1
  156. package/lib/sections/social/mutations.js +23 -132
  157. package/lib/specialButtons/AddMeToYourFriends.css +54 -0
  158. package/lib/specialButtons/addContact/AddMeToYourContacts.css +1118 -0
  159. package/lib/specialButtons/addContact/ContactCreationDialog.d.ts +10 -0
  160. package/lib/specialButtons/addContact/ContactCreationDialog.d.ts.map +1 -0
  161. package/lib/specialButtons/addContact/ContactCreationDialog.js +1123 -0
  162. package/lib/specialButtons/addContact/addMeToYourContacts.d.ts +16 -0
  163. package/lib/specialButtons/addContact/addMeToYourContacts.d.ts.map +1 -0
  164. package/lib/specialButtons/addContact/addMeToYourContacts.js +136 -0
  165. package/lib/specialButtons/addContact/contactsErrors.d.ts +8 -0
  166. package/lib/specialButtons/addContact/contactsErrors.d.ts.map +1 -0
  167. package/lib/specialButtons/addContact/contactsErrors.js +106 -0
  168. package/lib/specialButtons/addContact/contactsTypes.d.ts +43 -0
  169. package/lib/specialButtons/addContact/contactsTypes.d.ts.map +1 -0
  170. package/lib/specialButtons/addContact/contactsTypes.js +5 -0
  171. package/lib/specialButtons/addContact/helpers.d.ts +7 -0
  172. package/lib/specialButtons/addContact/helpers.d.ts.map +1 -0
  173. package/lib/specialButtons/addContact/helpers.js +103 -0
  174. package/lib/specialButtons/addContact/mutations.d.ts +16 -0
  175. package/lib/specialButtons/addContact/mutations.d.ts.map +1 -0
  176. package/lib/specialButtons/addContact/mutations.js +300 -0
  177. package/lib/specialButtons/addContact/selectors.d.ts +10 -0
  178. package/lib/specialButtons/addContact/selectors.d.ts.map +1 -0
  179. package/lib/specialButtons/addContact/selectors.js +163 -0
  180. package/lib/{addMeToYourFriends.d.ts → specialButtons/addMeToYourFriends.d.ts} +6 -4
  181. package/lib/specialButtons/addMeToYourFriends.d.ts.map +1 -0
  182. package/lib/{addMeToYourFriends.js → specialButtons/addMeToYourFriends.js} +46 -11
  183. package/lib/styles/CollapsibleSection.css +519 -0
  184. package/lib/styles/EditDialogs.css +506 -686
  185. package/lib/styles/EditDialogs.responsive.css +989 -0
  186. package/lib/texts/buttonTexts.d.ts +9 -0
  187. package/lib/texts/buttonTexts.d.ts.map +1 -0
  188. package/lib/texts/buttonTexts.js +14 -0
  189. package/lib/texts/dialogTexts.d.ts +14 -0
  190. package/lib/texts/dialogTexts.d.ts.map +1 -0
  191. package/lib/texts/dialogTexts.js +19 -0
  192. package/lib/texts/messageTexts.d.ts +42 -0
  193. package/lib/texts/messageTexts.d.ts.map +1 -0
  194. package/lib/texts/messageTexts.js +47 -0
  195. package/lib/texts/profileTexts.d.ts +14 -0
  196. package/lib/texts/profileTexts.d.ts.map +1 -0
  197. package/lib/texts/profileTexts.js +19 -0
  198. package/lib/texts/qrCodeTexts.d.ts +2 -0
  199. package/lib/texts/qrCodeTexts.d.ts.map +1 -0
  200. package/lib/texts/qrCodeTexts.js +7 -0
  201. package/lib/texts.d.ts +5 -60
  202. package/lib/texts.d.ts.map +1 -1
  203. package/lib/texts.js +55 -70
  204. package/lib/ui/dialog.css +233 -0
  205. package/lib/ui/dialog.d.ts +15 -1
  206. package/lib/ui/dialog.d.ts.map +1 -1
  207. package/lib/ui/dialog.js +245 -45
  208. package/lib/ui/dialog.responsive.css +195 -0
  209. package/lib/ui/errors.d.ts.map +1 -1
  210. package/lib/ui/errors.js +2 -1
  211. package/lib/ui/spinner.d.ts +3 -0
  212. package/lib/ui/spinner.d.ts.map +1 -0
  213. package/lib/ui/spinner.js +13 -0
  214. package/lib/utils/debug.d.ts +5 -0
  215. package/lib/utils/debug.d.ts.map +1 -0
  216. package/lib/utils/debug.js +23 -0
  217. package/lib/utils/errorDisplay.d.ts +2 -0
  218. package/lib/utils/errorDisplay.d.ts.map +1 -0
  219. package/lib/utils/errorDisplay.js +19 -0
  220. package/package.json +32 -25
  221. package/lib/ChatWithMe.d.ts +0 -7
  222. package/lib/ChatWithMe.d.ts.map +0 -1
  223. package/lib/ChatWithMe.js +0 -90
  224. package/lib/QRCodeCard.d.ts.map +0 -1
  225. package/lib/addMeToYourFriends.d.ts.map +0 -1
  226. package/lib/sections/heading/camera.d.ts +0 -19
  227. package/lib/sections/heading/camera.d.ts.map +0 -1
  228. package/lib/sections/heading/camera.js +0 -199
  229. package/lib/styles/BioSection.css +0 -77
  230. package/lib/styles/CVCard.css +0 -142
  231. package/lib/styles/ChatWithMe.css +0 -6
  232. package/lib/styles/ContactInfoEditDialog.css +0 -153
  233. package/lib/styles/EducationCard.css +0 -103
  234. package/lib/styles/HeadingSection.css +0 -309
  235. package/lib/styles/ProfileCard.css +0 -66
  236. package/lib/styles/ProfileView.css +0 -65
  237. package/lib/styles/ProjectsCard.css +0 -206
  238. package/lib/styles/QRCodeCard.css +0 -43
  239. package/lib/styles/SocialCard.css +0 -89
  240. package/lib/styles/dialog.css +0 -209
  241. package/lib/styles/utilities.css +0 -740
@@ -6,8 +6,10 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.createLanguageEditDialog = createLanguageEditDialog;
7
7
  var _dialog = require("../../ui/dialog");
8
8
  var _litHtml = require("lit-html");
9
+ require("solid-ui/components/actions/button");
10
+ require("solid-ui/components/forms/combobox");
9
11
  require("../../styles/EditDialogs.css");
10
- require("../../styles/ContactInfoEditDialog.css");
12
+ require("../contactInfo/ContactInfoEditDialog.css");
11
13
  var _rowState = require("../shared/rowState");
12
14
  var _textUtils = require("../../textUtils");
13
15
  var _mutations = require("./mutations");
@@ -24,9 +26,6 @@ function rowHasContent(row) {
24
26
  function normalizeLanguageCode(value) {
25
27
  return (value || '').trim().toLowerCase();
26
28
  }
27
- function normalizeSuggestionKey(value) {
28
- return sanitizeLanguageFieldValue(value).toLowerCase().replace(/\(.*?\)/g, ' ').replace(/[^a-z\s-]/g, ' ').replace(/\s+/g, ' ').trim();
29
- }
30
29
  function escapeSparqlRegex(value) {
31
30
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
32
31
  }
@@ -89,14 +88,62 @@ async function fetchLanguageSuggestions(term) {
89
88
  return [];
90
89
  }
91
90
  }
92
- function matchLanguageSuggestion(suggestions, value) {
93
- const normalized = sanitizeLanguageFieldValue(value).toLowerCase();
94
- if (!normalized) return null;
95
- const exact = suggestions.find(suggestion => suggestion.name.toLowerCase() === normalized);
96
- if (exact) return exact;
97
- const normalizedKey = normalizeSuggestionKey(value);
98
- if (!normalizedKey) return null;
99
- return suggestions.find(suggestion => normalizeSuggestionKey(suggestion.name) === normalizedKey) || null;
91
+ function toLanguageComboboxOption(suggestion) {
92
+ return {
93
+ label: suggestion.name,
94
+ value: suggestion.publicId,
95
+ publicId: suggestion.publicId,
96
+ meta: {
97
+ code: suggestion.code
98
+ }
99
+ };
100
+ }
101
+ function readLanguageComboboxInputValue(event) {
102
+ const customEvent = event;
103
+ if (typeof customEvent.detail?.value === 'string') {
104
+ return customEvent.detail.value;
105
+ }
106
+ const target = event.target;
107
+ return typeof target?.value === 'string' ? target.value : '';
108
+ }
109
+ function readLanguageComboboxChange(event) {
110
+ const customEvent = event;
111
+ if (customEvent.detail?.option) {
112
+ return customEvent.detail.option;
113
+ }
114
+ if (typeof customEvent.detail?.value === 'string') {
115
+ return {
116
+ label: typeof customEvent.detail?.label === 'string' ? customEvent.detail.label : '',
117
+ value: customEvent.detail.value,
118
+ publicId: customEvent.detail.value
119
+ };
120
+ }
121
+ return null;
122
+ }
123
+ function createLanguageSuggestionProvider() {
124
+ return async query => {
125
+ const suggestions = await fetchLanguageSuggestions(query);
126
+ return suggestions.map(toLanguageComboboxOption);
127
+ };
128
+ }
129
+ function initializeLanguageComboboxes(form, rows) {
130
+ const comboboxElements = form.querySelectorAll('solid-ui-combobox[data-language-row-index]');
131
+ comboboxElements.forEach(comboboxElement => {
132
+ const rowIndex = Number(comboboxElement.dataset.languageRowIndex);
133
+ if (Number.isNaN(rowIndex)) return;
134
+ const row = rows[rowIndex];
135
+ const options = row?.publicId && row?.name ? [{
136
+ label: row.name,
137
+ value: row.publicId,
138
+ publicId: row.publicId
139
+ }] : [];
140
+ comboboxElement.suggestionProvider = createLanguageSuggestionProvider();
141
+ comboboxElement.options = options;
142
+ comboboxElement.value = row?.publicId || '';
143
+ comboboxElement.inputValue = row?.name || '';
144
+ comboboxElement.label = '';
145
+ comboboxElement.placeholder = 'Language';
146
+ });
100
147
  }
101
148
  function toFormState(details) {
102
149
  const rows = (details || []).map(detail => ({
@@ -125,11 +172,7 @@ function hasOrderChanged(rows, initialExistingOrder) {
125
172
  }
126
173
  return false;
127
174
  }
128
- function validateLanguagesBeforeSave(rows, initialExistingOrder) {
129
- const ops = (0, _rowState.summarizeRowOps)(rows, rowHasContent);
130
- const hasChanges = ops.create.length > 0 || ops.update.length > 0 || ops.remove.length > 0;
131
- const orderChanged = hasOrderChanged(rows, initialExistingOrder);
132
- if (!hasChanges && !orderChanged) return 'No language changes detected.';
175
+ function validateLanguagesBeforeSave(rows) {
133
176
  for (let i = 0; i < rows.length; i++) {
134
177
  const row = rows[i];
135
178
  if (!row || row.status === 'deleted') continue;
@@ -148,20 +191,35 @@ function hasInvalidLanguageSelection(rows) {
148
191
  });
149
192
  }
150
193
  function updateLanguagesSubmitEnabled(rows) {
151
- const submitButton = document.querySelector('#profile-modal #modal-buttons button.btn-primary');
194
+ const submitButton = (0, _dialog.getSharedDialogSaveButton)(document);
152
195
  if (!submitButton) return;
153
196
  submitButton.disabled = hasInvalidLanguageSelection(rows);
154
197
  }
155
198
  function focusLanguageField(form, selector) {
156
199
  const nextField = form.querySelector(selector);
157
200
  if (!nextField || typeof nextField.focus !== 'function') return;
158
- nextField.scrollIntoView({
159
- block: 'start',
160
- behavior: 'auto'
161
- });
162
- nextField.focus();
163
- if (nextField instanceof HTMLInputElement || nextField instanceof HTMLTextAreaElement) {
164
- nextField.select();
201
+ if (typeof nextField.scrollIntoView === 'function') {
202
+ nextField.scrollIntoView({
203
+ block: 'start',
204
+ behavior: 'auto'
205
+ });
206
+ }
207
+ const comboboxHost = nextField.tagName === 'SOLID-UI-COMBOBOX' ? nextField : null;
208
+ const comboboxInput = comboboxHost ? comboboxHost.shadowRoot?.querySelector('input') : null;
209
+ const focusTarget = comboboxInput || nextField;
210
+ focusTarget.focus();
211
+ if (focusTarget instanceof HTMLInputElement) {
212
+ const caretPosition = 0;
213
+ focusTarget.setSelectionRange(caretPosition, caretPosition);
214
+ focusTarget.scrollLeft = 0;
215
+ requestAnimationFrame(() => {
216
+ focusTarget.scrollLeft = 0;
217
+ });
218
+ }
219
+ if (comboboxHost?._closePopup) {
220
+ requestAnimationFrame(() => {
221
+ comboboxHost._closePopup?.();
222
+ });
165
223
  }
166
224
  }
167
225
  function renderLanguageInputRow({
@@ -169,46 +227,44 @@ function renderLanguageInputRow({
169
227
  index,
170
228
  displayIndex,
171
229
  onDelete,
172
- onChange,
173
- onSearch,
174
- suggestions,
175
230
  onDragStart,
231
+ onDragEnter,
176
232
  onDragOver,
177
233
  onDrop,
178
234
  onDragEnd,
235
+ onPointerDown,
236
+ onPointerMove,
237
+ onPointerUp,
238
+ onPointerCancel,
179
239
  isDropTarget
180
240
  }) {
181
241
  const row = rows[index];
182
242
  const label = `Language ${displayIndex + 1}`;
183
243
  const proficiencyLabel = `Language Proficiency ${displayIndex + 1}`;
184
- const languageName = `language-${index}`;
185
244
  const proficiencyInputName = `proficiency-${index}`;
186
245
  const proficiencySelectId = `proficiency-${index}`;
187
- const datalistId = `language-suggestions-${index}`;
188
246
  const hasSelectionIssue = Boolean(row && row.status !== 'deleted' && (0, _textUtils.hasNonEmptyText)(row.name) && !(0, _textUtils.hasNonEmptyText)(row.publicId));
189
247
  const handleLanguageInput = field => e => {
190
- const target = e.target;
191
- const nextValue = sanitizeLanguageFieldValue(target.value);
248
+ const nextValue = sanitizeLanguageFieldValue(readLanguageComboboxInputValue(e));
192
249
  if (rows[index]) {
193
250
  (0, _rowState.applyRowFieldChange)(rows[index], field, nextValue, rowHasContent);
194
- const matchedSuggestion = matchLanguageSuggestion(suggestions, nextValue);
195
- rows[index].publicId = matchedSuggestion?.publicId || '';
196
- if (matchedSuggestion) {
197
- rows[index].name = sanitizeLanguageFieldValue(matchedSuggestion.name);
198
- }
199
- onSearch(index, nextValue);
200
- onChange();
251
+ rows[index].publicId = '';
252
+ updateLanguagesSubmitEnabled(rows);
201
253
  }
202
254
  };
203
255
  const handleLanguageChange = field => e => {
204
- handleLanguageInput(field)(e);
256
+ const selectedOption = readLanguageComboboxChange(e);
257
+ if (!rows[index] || !selectedOption) return;
258
+ (0, _rowState.applyRowFieldChange)(rows[index], field, sanitizeLanguageFieldValue(selectedOption.label), rowHasContent);
259
+ rows[index].publicId = selectedOption.publicId || selectedOption.value || '';
260
+ updateLanguagesSubmitEnabled(rows);
205
261
  };
206
262
  const handleProficiencyInput = e => {
207
263
  const target = e.target;
208
264
  const nextType = target.value;
209
265
  if (rows[index]) {
210
266
  (0, _rowState.applyRowSelectChange)(rows[index], 'proficiency', nextType);
211
- onChange();
267
+ updateLanguagesSubmitEnabled(rows);
212
268
  }
213
269
  };
214
270
  const handleDelete = event => {
@@ -218,41 +274,36 @@ function renderLanguageInputRow({
218
274
  return (0, _litHtml.html)`
219
275
  <div
220
276
  class="profile-edit-dialog__row profile-edit-dialog__row--language ${isDropTarget ? 'profile-edit-dialog__row--drop-target' : ''}"
221
- @dragover=${event => onDragOver(event)}
222
- @drop=${() => onDrop(index)}
277
+ data-language-reorder-index=${String(index)}
278
+ @dragenter=${() => onDragEnter(index)}
279
+ @dragover=${event => onDragOver(event, index)}
280
+ @drop=${event => onDrop(event, index)}
223
281
  >
224
- <button
282
+ <solid-ui-button
225
283
  type="button"
226
284
  class="profile-edit-dialog__drag-handle"
285
+ variant="icon"
286
+ size="md"
227
287
  aria-label=${`Reorder language ${displayIndex + 1}`}
228
288
  title="Drag to reorder"
229
289
  draggable="true"
230
- @dragstart=${() => onDragStart(index)}
290
+ @dragstart=${event => onDragStart(event, index)}
231
291
  @dragend=${() => onDragEnd()}
292
+ @pointerdown=${event => onPointerDown(event, index)}
293
+ @pointermove=${event => onPointerMove(event)}
294
+ @pointerup=${event => onPointerUp(event)}
295
+ @pointercancel=${event => onPointerCancel(event)}
232
296
  >
233
- ${_profileIcons.bentoIcon}
234
- </button>
235
- <label aria-label=${`${label} Language`} class="label profile-edit-dialog__field">
236
- <input
237
- class="input"
238
- type="text"
239
- name=${languageName}
240
- .value=${row?.name || ''}
241
- required
242
- data-contact-field="name"
243
- data-entry-node=${row?.entryNode || ''}
244
- data-row-status=${row?.status || 'n/a'}
245
- placeholder="Language"
246
- autocomplete="off"
247
- list=${datalistId}
248
- inputmode="text"
297
+ <span slot="icon" class="profile-edit-dialog__drag-handle-icon" aria-hidden="true">${_profileIcons.bentoIcon}</span>
298
+ </solid-ui-button>
299
+ <label aria-label=${`${label} Language`} class="label profile-edit-dialog__field profile-edit-dialog__field--language-name">
300
+ <solid-ui-combobox
301
+ aria-label=${`${label} Language`}
302
+ data-language-row-index=${String(index)}
249
303
  aria-invalid=${hasSelectionIssue ? 'true' : 'false'}
250
304
  @input=${handleLanguageInput('name')}
251
305
  @change=${handleLanguageChange('name')}
252
- />
253
- <datalist id=${datalistId}>
254
- ${suggestions.map(suggestion => (0, _litHtml.html)`<option value=${suggestion.name}></option>`)}
255
- </datalist>
306
+ ></solid-ui-combobox>
256
307
  <small class="profile-edit-dialog__input-help-text">Type to search and select one language suggestion.</small>
257
308
  </label>
258
309
  <label aria-label=${proficiencyLabel} class="label" hidden>
@@ -262,46 +313,60 @@ function renderLanguageInputRow({
262
313
  <option value="Fluent">Fluent</option>
263
314
  </select>
264
315
  </label>
265
- <div class="profile-edit-dialog__actions profile-edit-dialog__actions--edge">
266
- <button
316
+ <div class="profile-edit-dialog__actions profile-edit-dialog__actions--edge profile-edit-dialog__actions--language">
317
+ <solid-ui-button
267
318
  type="button"
319
+ variant="icon"
320
+ size="md"
268
321
  class="profile-edit-dialog__delete-button"
269
322
  aria-label=${`Delete language ${displayIndex + 1}`}
270
323
  title=${_texts.deleteEntryButtonTitleText}
271
324
  @click=${handleDelete}
272
325
  >
273
- <span class="profile-edit-dialog__delete-icon" aria-hidden="true">${_profileIcons.trashIcon}</span>
274
- </button>
326
+ <span slot="icon" class="profile-edit-dialog__delete-icon" aria-hidden="true">${_profileIcons.trashIcon}</span>
327
+ </solid-ui-button>
275
328
  </div>
276
329
  </div>
277
330
  `;
278
331
  }
279
- function renderLanguageSection(rows, onAddRow, suggestionByIndex, onSearch) {
332
+ function renderLanguageSection(rows, onAddRow) {
280
333
  let dragSourceIndex = null;
281
334
  let dropTargetIndex = null;
335
+ let touchPointerId = null;
282
336
  const reorderRows = (from, to) => {
283
337
  if (from === to) return;
284
338
  const row = rows[from];
285
339
  if (!row) return;
286
340
  rows.splice(from, 1);
287
341
  rows.splice(to, 0, row);
288
- Object.keys(suggestionByIndex).forEach(key => {
289
- delete suggestionByIndex[Number(key)];
290
- });
291
342
  };
292
- const handleDragStart = index => {
343
+ const handleDragStart = (event, index) => {
293
344
  dragSourceIndex = index;
294
345
  dropTargetIndex = index;
346
+ if (event.dataTransfer) {
347
+ event.dataTransfer.effectAllowed = 'move';
348
+ event.dataTransfer.setData('text/plain', String(index));
349
+ }
350
+ };
351
+ const handleDragEnter = index => {
352
+ if (dragSourceIndex === null) return;
353
+ dropTargetIndex = index;
295
354
  };
296
- const handleDragOver = event => {
355
+ const handleDragOver = (event, index) => {
297
356
  event.preventDefault();
357
+ if (dragSourceIndex !== null) {
358
+ dropTargetIndex = index;
359
+ }
298
360
  if (event.dataTransfer) {
299
361
  event.dataTransfer.dropEffect = 'move';
300
362
  }
301
363
  };
302
- const handleDrop = index => {
303
- if (dragSourceIndex === null) return;
304
- reorderRows(dragSourceIndex, index);
364
+ const handleDrop = (event, index) => {
365
+ event.preventDefault();
366
+ const transferIndex = event.dataTransfer?.getData('text/plain');
367
+ const sourceIndex = dragSourceIndex ?? (transferIndex ? Number(transferIndex) : null);
368
+ if (sourceIndex === null || Number.isNaN(sourceIndex)) return;
369
+ reorderRows(sourceIndex, index);
305
370
  dragSourceIndex = null;
306
371
  dropTargetIndex = null;
307
372
  onAddRow();
@@ -310,6 +375,49 @@ function renderLanguageSection(rows, onAddRow, suggestionByIndex, onSearch) {
310
375
  dragSourceIndex = null;
311
376
  dropTargetIndex = null;
312
377
  };
378
+ const updateTouchDropTarget = event => {
379
+ const dom = event.currentTarget?.ownerDocument || document;
380
+ const target = dom.elementFromPoint(event.clientX, event.clientY)?.closest('[data-language-reorder-index]');
381
+ if (!target) return;
382
+ const nextIndex = Number(target.dataset.languageReorderIndex);
383
+ if (!Number.isNaN(nextIndex)) {
384
+ dropTargetIndex = nextIndex;
385
+ }
386
+ };
387
+ const handlePointerDown = (event, index) => {
388
+ if (event.pointerType !== 'touch') return;
389
+ event.preventDefault();
390
+ dragSourceIndex = index;
391
+ dropTargetIndex = index;
392
+ touchPointerId = event.pointerId;
393
+ event.currentTarget?.setPointerCapture?.(event.pointerId);
394
+ };
395
+ const handlePointerMove = event => {
396
+ if (event.pointerType !== 'touch' || touchPointerId !== event.pointerId || dragSourceIndex === null) return;
397
+ event.preventDefault();
398
+ updateTouchDropTarget(event);
399
+ };
400
+ const handlePointerUp = event => {
401
+ if (event.pointerType !== 'touch' || touchPointerId !== event.pointerId) return;
402
+ event.preventDefault();
403
+ updateTouchDropTarget(event);
404
+ const sourceIndex = dragSourceIndex;
405
+ const targetIndex = dropTargetIndex;
406
+ event.currentTarget?.releasePointerCapture?.(event.pointerId);
407
+ touchPointerId = null;
408
+ dragSourceIndex = null;
409
+ dropTargetIndex = null;
410
+ if (sourceIndex === null || targetIndex === null || sourceIndex === targetIndex) return;
411
+ reorderRows(sourceIndex, targetIndex);
412
+ onAddRow();
413
+ };
414
+ const handlePointerCancel = event => {
415
+ if (event.pointerType !== 'touch' || touchPointerId !== event.pointerId) return;
416
+ event.currentTarget?.releasePointerCapture?.(event.pointerId);
417
+ touchPointerId = null;
418
+ dragSourceIndex = null;
419
+ dropTargetIndex = null;
420
+ };
313
421
  const visibleRows = rows.map((row, index) => ({
314
422
  row,
315
423
  index
@@ -326,17 +434,18 @@ function renderLanguageSection(rows, onAddRow, suggestionByIndex, onSearch) {
326
434
  rows,
327
435
  index,
328
436
  displayIndex,
329
- onChange: onAddRow,
330
- onSearch,
331
- suggestions: suggestionByIndex[index] || [],
332
437
  onDragStart: handleDragStart,
438
+ onDragEnter: handleDragEnter,
333
439
  onDragOver: handleDragOver,
334
440
  onDrop: handleDrop,
335
441
  onDragEnd: handleDragEnd,
442
+ onPointerDown: handlePointerDown,
443
+ onPointerMove: handlePointerMove,
444
+ onPointerUp: handlePointerUp,
445
+ onPointerCancel: handlePointerCancel,
336
446
  isDropTarget: dropTargetIndex === index,
337
447
  onDelete: () => {
338
448
  (0, _rowState.deleteRow)(rows, index);
339
- delete suggestionByIndex[index];
340
449
  onAddRow();
341
450
  }
342
451
  }))}
@@ -345,39 +454,12 @@ function renderLanguageSection(rows, onAddRow, suggestionByIndex, onSearch) {
345
454
  `;
346
455
  }
347
456
  function renderLanguageEditTemplate(form, formState, viewerMode, options = {}) {
348
- const formStateWithSearch = formState;
349
- const suggestionByIndex = formStateWithSearch.suggestionByIndex || (formStateWithSearch.suggestionByIndex = {});
350
- const searchSeqByIndex = formStateWithSearch.searchSeqByIndex || (formStateWithSearch.searchSeqByIndex = {});
351
- const searchTimerByIndex = formStateWithSearch.searchTimerByIndex || (formStateWithSearch.searchTimerByIndex = {});
352
457
  const rerender = (nextOptions = {}) => renderLanguageEditTemplate(form, formState, viewerMode, nextOptions);
353
- const onSearch = (index, term) => {
354
- if (searchTimerByIndex[index]) {
355
- clearTimeout(searchTimerByIndex[index]);
356
- }
357
- const normalized = sanitizeLanguageFieldValue(term);
358
- if (normalized.length < 2) {
359
- suggestionByIndex[index] = [];
360
- rerender();
361
- return;
362
- }
363
- const seq = (searchSeqByIndex[index] || 0) + 1;
364
- searchSeqByIndex[index] = seq;
365
- searchTimerByIndex[index] = setTimeout(async () => {
366
- const suggestions = await fetchLanguageSuggestions(normalized);
367
- if (searchSeqByIndex[index] !== seq) return;
368
- suggestionByIndex[index] = suggestions;
369
- const row = formState.languages[index];
370
- if (row) {
371
- const matchedSuggestion = matchLanguageSuggestion(suggestions, row.name);
372
- row.publicId = matchedSuggestion?.publicId || row.publicId;
373
- }
374
- rerender();
375
- }, 220);
376
- };
377
458
  (0, _litHtml.render)((0, _litHtml.html)`
378
- ${renderLanguageSection(formState.languages, rerender, suggestionByIndex, onSearch)}
459
+ ${renderLanguageSection(formState.languages, rerender)}
379
460
  ${viewerMode !== 'owner' ? (0, _litHtml.html)`<p class="profile-edit-dialog__login-message">${_texts.ownerLoginRequiredDialogMessageText}</p>` : null}
380
461
  `, form);
462
+ initializeLanguageComboboxes(form, formState.languages);
381
463
  updateLanguagesSubmitEnabled(formState.languages);
382
464
  if (options.focusSelector) {
383
465
  focusLanguageField(form, options.focusSelector);
@@ -397,12 +479,8 @@ function createLanguageEditForm(details, viewerMode) {
397
479
  entryNode: '',
398
480
  status: 'new'
399
481
  });
400
- const formStateWithSearch = formState;
401
- formStateWithSearch.suggestionByIndex = {};
402
- formStateWithSearch.searchSeqByIndex = {};
403
- formStateWithSearch.searchTimerByIndex = {};
404
482
  renderLanguageEditTemplate(form, formState, viewerMode, {
405
- focusSelector: '[name="language-0"]'
483
+ focusSelector: '[data-language-row-index="0"]'
406
484
  });
407
485
  };
408
486
  return {
@@ -422,6 +500,12 @@ async function createLanguageEditDialog(event, store, subject, languages, viewer
422
500
  title: _texts.editLanguagesDialogTitleText,
423
501
  dom,
424
502
  form,
503
+ onOpen: () => focusLanguageField(form, '[data-language-row-index="0"]'),
504
+ shouldCloseWithoutSave: () => {
505
+ const ops = (0, _rowState.summarizeRowOps)(formState.languages, rowHasContent);
506
+ const orderChanged = hasOrderChanged(formState.languages, formState.initialExistingOrder);
507
+ return ops.create.length === 0 && ops.update.length === 0 && ops.remove.length === 0 && !orderChanged;
508
+ },
425
509
  headerAction: {
426
510
  type: 'button',
427
511
  label: '+ Add More',
@@ -434,7 +518,7 @@ async function createLanguageEditDialog(event, store, subject, languages, viewer
434
518
  if (viewerMode !== 'owner') {
435
519
  return _texts.ownerLoginRequiredDialogMessageText;
436
520
  }
437
- return validateLanguagesBeforeSave(formState.languages, formState.initialExistingOrder);
521
+ return validateLanguagesBeforeSave(formState.languages);
438
522
  },
439
523
  onSave: async () => {
440
524
  const languageOps = (0, _rowState.summarizeRowOps)(formState.languages, rowHasContent);
@@ -446,8 +530,7 @@ async function createLanguageEditDialog(event, store, subject, languages, viewer
446
530
  await (0, _mutations.processLanguageMutations)(store, subject, plan, formState.languages);
447
531
  },
448
532
  formatSaveError: error => {
449
- const message = error instanceof Error ? error.message : String(error);
450
- return `${_texts.saveLanguageUpdatesFailedPrefixText} ${message}`;
533
+ return error instanceof Error ? error.message : _texts.saveLanguageUpdatesFailedMessageText;
451
534
  }
452
535
  });
453
536
  if (!result) return;
@@ -0,0 +1,53 @@
1
+ /* Languages list */
2
+ .languages__list {
3
+ display: flex;
4
+ flex-direction: column;
5
+ width: 100%;
6
+ max-width: 100%;
7
+ min-width: 0;
8
+ box-sizing: border-box;
9
+ list-style: none;
10
+ margin: 0;
11
+ padding: 0;
12
+ align-items: flex-start;
13
+ gap: var(--spacing-sm, 0.9375rem);
14
+ align-self: stretch;
15
+ }
16
+
17
+ .languages__item-label {
18
+ color: var(--color-text-subheading, var(--gray-900, #101828));
19
+ font-size: var(--font-size-md, 1rem);
20
+ font-weight: var(--font-weight-md, 500);
21
+ }
22
+
23
+ /* Empty state */
24
+ .languages__empty-icon {
25
+ display: inline-flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ width: var(--icon-xs, 1.5rem);
29
+ height: var(--icon-xs, 1.5rem);
30
+ flex-shrink: 0;
31
+ }
32
+
33
+ .languages__empty-icon svg {
34
+ display: block;
35
+ width: 100%;
36
+ height: 100%;
37
+ }
38
+
39
+ :is(.profile-pane-host, .profile-pane-root)[data-layout='mobile'] .languages__item-label {
40
+ font-size: var(--font-size-sm, 0.875rem);
41
+ }
42
+
43
+ @media (max-width: 576px) {
44
+ .languages__item-label {
45
+ font-size: var(--font-size-sm, 0.875rem);
46
+ }
47
+ }
48
+
49
+ @container profile-pane (max-width: 576px) {
50
+ .languages__item-label {
51
+ font-size: var(--font-size-sm, 0.875rem);
52
+ }
53
+ }
@@ -1,5 +1,7 @@
1
+ import 'solid-ui/components/actions/button';
1
2
  import { LiveStore, NamedNode } from 'rdflib';
2
3
  import { ViewerMode } from '../../types';
4
+ import './LanguageSection.css';
3
5
  import { LanguageDetails } from './types';
4
6
  export declare function renderLanguageSection(store: LiveStore, subject: NamedNode, languages: LanguageDetails[], viewerMode: ViewerMode, onSaved?: () => Promise<void> | void): import("lit-html").TemplateResult<1> | "";
5
7
  //# sourceMappingURL=LanguageSection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LanguageSection.d.ts","sourceRoot":"","sources":["../../../src/sections/languages/LanguageSection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAwJzC,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,SAAS,EAClB,SAAS,EAAE,eAAe,EAAE,EAC5B,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,6CAcrC"}
1
+ {"version":3,"file":"LanguageSection.d.ts","sourceRoot":"","sources":["../../../src/sections/languages/LanguageSection.ts"],"names":[],"mappings":"AACA,OAAO,oCAAoC,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,uBAAuB,CAAA;AAE9B,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAiKzC,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,SAAS,EAClB,SAAS,EAAE,eAAe,EAAE,EAC5B,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,6CAcrC"}