@universityofmaryland/web-feeds-library 1.3.0-beta.0 → 1.3.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/academic.js +4 -4
  2. package/dist/academic.js.map +1 -1
  3. package/dist/events.js +10 -10
  4. package/dist/events.js.map +1 -1
  5. package/dist/experts.js +8 -8
  6. package/dist/experts.js.map +1 -1
  7. package/dist/factory/core/createBaseFeed.d.ts.map +1 -1
  8. package/dist/factory/core/createBaseFeed.js +19 -17
  9. package/dist/factory/core/createBaseFeed.js.map +1 -1
  10. package/dist/factory/core/types.d.ts +1 -0
  11. package/dist/factory/core/types.d.ts.map +1 -1
  12. package/dist/factory/helpers/displayHandler.js +29 -47
  13. package/dist/factory/helpers/displayHandler.js.map +1 -1
  14. package/dist/factory/helpers/feedHelpers.js +3 -3
  15. package/dist/factory/helpers/feedHelpers.js.map +1 -1
  16. package/dist/factory/helpers/fetchHandler.js +16 -33
  17. package/dist/factory/helpers/fetchHandler.js.map +1 -1
  18. package/dist/feeds/academic/index.d.ts +1 -1
  19. package/dist/feeds/academic/index.d.ts.map +1 -1
  20. package/dist/feeds/academic/slider.d.ts +1 -2
  21. package/dist/feeds/academic/slider.d.ts.map +1 -1
  22. package/dist/feeds/academic/slider.js +7 -6
  23. package/dist/feeds/academic/slider.js.map +1 -1
  24. package/dist/feeds/events/grid.d.ts +1 -2
  25. package/dist/feeds/events/grid.d.ts.map +1 -1
  26. package/dist/feeds/events/grid.js +22 -21
  27. package/dist/feeds/events/grid.js.map +1 -1
  28. package/dist/feeds/events/grouped.d.ts +1 -2
  29. package/dist/feeds/events/grouped.d.ts.map +1 -1
  30. package/dist/feeds/events/grouped.js +62 -78
  31. package/dist/feeds/events/grouped.js.map +1 -1
  32. package/dist/feeds/events/index.d.ts +4 -4
  33. package/dist/feeds/events/index.d.ts.map +1 -1
  34. package/dist/feeds/events/list.d.ts +1 -2
  35. package/dist/feeds/events/list.d.ts.map +1 -1
  36. package/dist/feeds/events/list.js +22 -21
  37. package/dist/feeds/events/list.js.map +1 -1
  38. package/dist/feeds/events/slider.d.ts +1 -2
  39. package/dist/feeds/events/slider.d.ts.map +1 -1
  40. package/dist/feeds/events/slider.js +7 -6
  41. package/dist/feeds/events/slider.js.map +1 -1
  42. package/dist/feeds/experts/_types.d.ts +2 -1
  43. package/dist/feeds/experts/_types.d.ts.map +1 -1
  44. package/dist/feeds/experts/bio.d.ts +1 -2
  45. package/dist/feeds/experts/bio.d.ts.map +1 -1
  46. package/dist/feeds/experts/bio.js +32 -31
  47. package/dist/feeds/experts/bio.js.map +1 -1
  48. package/dist/feeds/experts/grid.d.ts +1 -2
  49. package/dist/feeds/experts/grid.d.ts.map +1 -1
  50. package/dist/feeds/experts/grid.js +24 -23
  51. package/dist/feeds/experts/grid.js.map +1 -1
  52. package/dist/feeds/experts/index.d.ts +3 -3
  53. package/dist/feeds/experts/index.d.ts.map +1 -1
  54. package/dist/feeds/experts/list.d.ts +1 -2
  55. package/dist/feeds/experts/list.d.ts.map +1 -1
  56. package/dist/feeds/experts/list.js +23 -22
  57. package/dist/feeds/experts/list.js.map +1 -1
  58. package/dist/feeds/news/featured.d.ts +1 -2
  59. package/dist/feeds/news/featured.d.ts.map +1 -1
  60. package/dist/feeds/news/featured.js +56 -55
  61. package/dist/feeds/news/featured.js.map +1 -1
  62. package/dist/feeds/news/grid.d.ts +1 -2
  63. package/dist/feeds/news/grid.d.ts.map +1 -1
  64. package/dist/feeds/news/grid.js +24 -23
  65. package/dist/feeds/news/grid.js.map +1 -1
  66. package/dist/feeds/news/index.d.ts +3 -3
  67. package/dist/feeds/news/index.d.ts.map +1 -1
  68. package/dist/feeds/news/list.d.ts +1 -2
  69. package/dist/feeds/news/list.d.ts.map +1 -1
  70. package/dist/feeds/news/list.js +23 -22
  71. package/dist/feeds/news/list.js.map +1 -1
  72. package/dist/helpers/events/index.js +4 -4
  73. package/dist/helpers/events/index.js.map +1 -1
  74. package/dist/helpers/grouping/events.js +10 -10
  75. package/dist/helpers/grouping/events.js.map +1 -1
  76. package/dist/helpers/styles/shadow.js +5 -22
  77. package/dist/helpers/styles/shadow.js.map +1 -1
  78. package/dist/index.js +10 -10
  79. package/dist/index.js.map +1 -1
  80. package/dist/news.js +8 -8
  81. package/dist/news.js.map +1 -1
  82. package/dist/states/_types.d.ts +0 -24
  83. package/dist/states/_types.d.ts.map +1 -1
  84. package/dist/states/_types.js +3 -3
  85. package/dist/states/_types.js.map +1 -1
  86. package/dist/states/announcer.d.ts +1 -3
  87. package/dist/states/announcer.d.ts.map +1 -1
  88. package/dist/states/announcer.js +5 -5
  89. package/dist/states/announcer.js.map +1 -1
  90. package/dist/states/empty.d.ts +0 -2
  91. package/dist/states/empty.d.ts.map +1 -1
  92. package/dist/states/empty.js +15 -32
  93. package/dist/states/empty.js.map +1 -1
  94. package/dist/states/index.d.ts +4 -8
  95. package/dist/states/index.d.ts.map +1 -1
  96. package/dist/states/loading.d.ts +1 -3
  97. package/dist/states/loading.d.ts.map +1 -1
  98. package/dist/states/loading.js +16 -16
  99. package/dist/states/loading.js.map +1 -1
  100. package/dist/states/pagination.d.ts +1 -3
  101. package/dist/states/pagination.d.ts.map +1 -1
  102. package/dist/states/pagination.js +11 -28
  103. package/dist/states/pagination.js.map +1 -1
  104. package/dist/strategies/display/events.js +13 -13
  105. package/dist/strategies/display/events.js.map +1 -1
  106. package/dist/strategies/display/experts.js +23 -23
  107. package/dist/strategies/display/experts.js.map +1 -1
  108. package/dist/strategies/display/news.js +13 -13
  109. package/dist/strategies/display/news.js.map +1 -1
  110. package/dist/strategies/fetch/academic.js +3 -3
  111. package/dist/strategies/fetch/academic.js.map +1 -1
  112. package/dist/strategies/fetch/events.js +13 -13
  113. package/dist/strategies/fetch/events.js.map +1 -1
  114. package/dist/strategies/fetch/experts.d.ts +1 -1
  115. package/dist/strategies/fetch/experts.d.ts.map +1 -1
  116. package/dist/strategies/fetch/experts.js +13 -8
  117. package/dist/strategies/fetch/experts.js.map +1 -1
  118. package/dist/strategies/fetch/graphql.d.ts.map +1 -1
  119. package/dist/strategies/fetch/graphql.js +11 -7
  120. package/dist/strategies/fetch/graphql.js.map +1 -1
  121. package/dist/strategies/fetch/news.js +6 -6
  122. package/dist/strategies/fetch/news.js.map +1 -1
  123. package/dist/strategies/layout/grid.js +11 -11
  124. package/dist/strategies/layout/grid.js.map +1 -1
  125. package/dist/widgets/index.d.ts +1 -1
  126. package/dist/widgets/index.d.ts.map +1 -1
  127. package/dist/widgets/slider.d.ts +1 -2
  128. package/dist/widgets/slider.d.ts.map +1 -1
  129. package/dist/widgets/slider.js +19 -35
  130. package/dist/widgets/slider.js.map +1 -1
  131. package/package.json +17 -14
  132. package/dist/academic.mjs +0 -5
  133. package/dist/academic.mjs.map +0 -1
  134. package/dist/events.mjs +0 -11
  135. package/dist/events.mjs.map +0 -1
  136. package/dist/experts.mjs +0 -9
  137. package/dist/experts.mjs.map +0 -1
  138. package/dist/factory/core/createBaseFeed.mjs +0 -114
  139. package/dist/factory/core/createBaseFeed.mjs.map +0 -1
  140. package/dist/factory/helpers/displayHandler.mjs +0 -169
  141. package/dist/factory/helpers/displayHandler.mjs.map +0 -1
  142. package/dist/factory/helpers/feedHelpers.mjs +0 -32
  143. package/dist/factory/helpers/feedHelpers.mjs.map +0 -1
  144. package/dist/factory/helpers/fetchHandler.mjs +0 -123
  145. package/dist/factory/helpers/fetchHandler.mjs.map +0 -1
  146. package/dist/feeds/academic/slider.mjs +0 -11
  147. package/dist/feeds/academic/slider.mjs.map +0 -1
  148. package/dist/feeds/events/grid.mjs +0 -32
  149. package/dist/feeds/events/grid.mjs.map +0 -1
  150. package/dist/feeds/events/grouped.mjs +0 -337
  151. package/dist/feeds/events/grouped.mjs.map +0 -1
  152. package/dist/feeds/events/list.mjs +0 -33
  153. package/dist/feeds/events/list.mjs.map +0 -1
  154. package/dist/feeds/events/slider.mjs +0 -11
  155. package/dist/feeds/events/slider.mjs.map +0 -1
  156. package/dist/feeds/experts/bio.mjs +0 -147
  157. package/dist/feeds/experts/bio.mjs.map +0 -1
  158. package/dist/feeds/experts/grid.mjs +0 -37
  159. package/dist/feeds/experts/grid.mjs.map +0 -1
  160. package/dist/feeds/experts/list.mjs +0 -26
  161. package/dist/feeds/experts/list.mjs.map +0 -1
  162. package/dist/feeds/news/featured.mjs +0 -379
  163. package/dist/feeds/news/featured.mjs.map +0 -1
  164. package/dist/feeds/news/grid.mjs +0 -37
  165. package/dist/feeds/news/grid.mjs.map +0 -1
  166. package/dist/feeds/news/list.mjs +0 -34
  167. package/dist/feeds/news/list.mjs.map +0 -1
  168. package/dist/helpers/events/index.mjs +0 -21
  169. package/dist/helpers/events/index.mjs.map +0 -1
  170. package/dist/helpers/grouping/events.mjs +0 -147
  171. package/dist/helpers/grouping/events.mjs.map +0 -1
  172. package/dist/helpers/styles/shadow.mjs +0 -16
  173. package/dist/helpers/styles/shadow.mjs.map +0 -1
  174. package/dist/index.mjs +0 -11
  175. package/dist/index.mjs.map +0 -1
  176. package/dist/news.mjs +0 -9
  177. package/dist/news.mjs.map +0 -1
  178. package/dist/states/_types.mjs +0 -12
  179. package/dist/states/_types.mjs.map +0 -1
  180. package/dist/states/announcer.mjs +0 -62
  181. package/dist/states/announcer.mjs.map +0 -1
  182. package/dist/states/empty.mjs +0 -104
  183. package/dist/states/empty.mjs.map +0 -1
  184. package/dist/states/loading.mjs +0 -155
  185. package/dist/states/loading.mjs.map +0 -1
  186. package/dist/states/pagination.mjs +0 -102
  187. package/dist/states/pagination.mjs.map +0 -1
  188. package/dist/strategies/display/events.mjs +0 -60
  189. package/dist/strategies/display/events.mjs.map +0 -1
  190. package/dist/strategies/display/experts.mjs +0 -266
  191. package/dist/strategies/display/experts.mjs.map +0 -1
  192. package/dist/strategies/display/news.mjs +0 -58
  193. package/dist/strategies/display/news.mjs.map +0 -1
  194. package/dist/strategies/fetch/academic.mjs +0 -30
  195. package/dist/strategies/fetch/academic.mjs.map +0 -1
  196. package/dist/strategies/fetch/events.mjs +0 -223
  197. package/dist/strategies/fetch/events.mjs.map +0 -1
  198. package/dist/strategies/fetch/experts.mjs +0 -189
  199. package/dist/strategies/fetch/experts.mjs.map +0 -1
  200. package/dist/strategies/fetch/graphql.mjs +0 -100
  201. package/dist/strategies/fetch/graphql.mjs.map +0 -1
  202. package/dist/strategies/fetch/news.mjs +0 -95
  203. package/dist/strategies/fetch/news.mjs.map +0 -1
  204. package/dist/strategies/layout/grid.mjs +0 -36
  205. package/dist/strategies/layout/grid.mjs.map +0 -1
  206. package/dist/widgets/slider.mjs +0 -87
  207. package/dist/widgets/slider.mjs.map +0 -1
@@ -1,147 +0,0 @@
1
- const MONTH_MAP = {
2
- Jan: "01",
3
- Feb: "02",
4
- Mar: "03",
5
- Apr: "04",
6
- May: "05",
7
- Jun: "06",
8
- Jul: "07",
9
- Aug: "08",
10
- Sep: "09",
11
- Oct: "10",
12
- Nov: "11",
13
- Dec: "12"
14
- };
15
- const getDateBanner = (dateStamp) => {
16
- const dateParts = dateStamp.split("T")[0].split("-");
17
- const year = parseInt(dateParts[0], 10);
18
- const month = parseInt(dateParts[1], 10) - 1;
19
- const day = parseInt(dateParts[2], 10);
20
- const eventDate = new Date(year, month, day);
21
- const currentDate = /* @__PURE__ */ new Date();
22
- currentDate.setHours(0, 0, 0, 0);
23
- const weekFromNow = /* @__PURE__ */ new Date();
24
- weekFromNow.setDate(currentDate.getDate() + 7);
25
- weekFromNow.setHours(0, 0, 0, 0);
26
- if (eventDate.getFullYear() === currentDate.getFullYear() && eventDate.getMonth() === currentDate.getMonth() && eventDate.getDate() === currentDate.getDate()) {
27
- return "Today";
28
- }
29
- if (eventDate.getTime() > currentDate.getTime() && eventDate.getTime() <= weekFromNow.getTime()) {
30
- const days2 = [
31
- "Sunday",
32
- "Monday",
33
- "Tuesday",
34
- "Wednesday",
35
- "Thursday",
36
- "Friday",
37
- "Saturday"
38
- ];
39
- return days2[eventDate.getDay()];
40
- }
41
- const days = [
42
- "Sunday",
43
- "Monday",
44
- "Tuesday",
45
- "Wednesday",
46
- "Thursday",
47
- "Friday",
48
- "Saturday"
49
- ];
50
- const months = [
51
- "Jan",
52
- "Feb",
53
- "Mar",
54
- "Apr",
55
- "May",
56
- "Jun",
57
- "Jul",
58
- "Aug",
59
- "Sep",
60
- "Oct",
61
- "Nov",
62
- "Dec"
63
- ];
64
- return `${days[eventDate.getDay()]}, ${months[eventDate.getMonth()]} ${eventDate.getDate()}`;
65
- };
66
- const parseLocalDate = (dateString) => {
67
- const parts = dateString.split("-");
68
- const date = new Date(
69
- parseInt(parts[0], 10),
70
- parseInt(parts[1], 10) - 1,
71
- parseInt(parts[2], 10)
72
- );
73
- date.setHours(0, 0, 0, 0);
74
- return date;
75
- };
76
- const getEventStartDate = (event) => {
77
- return parseLocalDate(event.startStamp.split("T")[0]);
78
- };
79
- const isMultiDayEvent = (event) => {
80
- const startParts = event.startStamp.split("T")[0].split("-");
81
- const startMonth = startParts[1];
82
- const startDay = startParts[2];
83
- const endMonth = MONTH_MAP[event.endMonth || ""];
84
- const endDay = (event.endDay || "").padStart(2, "0");
85
- return !(startMonth === endMonth && startDay === endDay);
86
- };
87
- const eventStartsOnDate = (event, targetDate) => {
88
- const eventStartDate = getEventStartDate(event);
89
- return eventStartDate.getTime() === targetDate.getTime();
90
- };
91
- const getEventPriority = (event, groupDate) => {
92
- const isMulti = isMultiDayEvent(event);
93
- const startsOnDate = eventStartsOnDate(event, groupDate);
94
- if (isMulti && startsOnDate) return 1;
95
- if (!isMulti) return 2;
96
- return 3;
97
- };
98
- const sortEventsByPriority = (events, groupDate) => {
99
- return [...events].sort((a, b) => {
100
- const aPriority = getEventPriority(a, groupDate);
101
- const bPriority = getEventPriority(b, groupDate);
102
- return aPriority - bPriority;
103
- });
104
- };
105
- const groupEventsByDate = (events) => {
106
- const currentDate = /* @__PURE__ */ new Date();
107
- currentDate.setHours(0, 0, 0, 0);
108
- const grouped = events.reduce((acc, event) => {
109
- const eventDate = getEventStartDate(event);
110
- const dateKey = eventDate < currentDate ? currentDate.toISOString().split("T")[0] : event.startStamp;
111
- if (!acc[dateKey]) {
112
- acc[dateKey] = {
113
- date: getDateBanner(dateKey),
114
- events: []
115
- };
116
- }
117
- acc[dateKey].events.push(event);
118
- return acc;
119
- }, {});
120
- Object.keys(grouped).forEach((dateKey) => {
121
- const groupDate = parseLocalDate(dateKey);
122
- grouped[dateKey].events = sortEventsByPriority(
123
- grouped[dateKey].events,
124
- groupDate
125
- );
126
- });
127
- return Object.values(grouped).sort((a, b) => {
128
- const dateA = new Date(
129
- Object.keys(grouped).find((key) => grouped[key] === a) || ""
130
- );
131
- const dateB = new Date(
132
- Object.keys(grouped).find((key) => grouped[key] === b) || ""
133
- );
134
- return dateA.getTime() - dateB.getTime();
135
- });
136
- };
137
- export {
138
- eventStartsOnDate,
139
- getDateBanner,
140
- getEventPriority,
141
- getEventStartDate,
142
- groupEventsByDate,
143
- isMultiDayEvent,
144
- parseLocalDate,
145
- sortEventsByPriority
146
- };
147
- //# sourceMappingURL=events.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"events.mjs","sources":["../../../source/helpers/grouping/events.ts"],"sourcesContent":["/**\n * Event Grouping Utilities\n *\n * Utilities for grouping and sorting events by date.\n * Used by the grouped events feed.\n *\n * @module utilities/grouping/events\n */\n\nimport { EventEntry } from 'types/data';\n\nexport interface GroupedEvent {\n date: string;\n events: EventEntry[];\n}\n\nconst MONTH_MAP: Record<string, string> = {\n Jan: '01',\n Feb: '02',\n Mar: '03',\n Apr: '04',\n May: '05',\n Jun: '06',\n Jul: '07',\n Aug: '08',\n Sep: '09',\n Oct: '10',\n Nov: '11',\n Dec: '12',\n};\n\n/**\n * Get a human-readable date banner\n *\n * Returns \"Today\", day name (within 7 days), or formatted date\n */\nexport const getDateBanner = (dateStamp: string): string => {\n // Parse the date string more reliably\n const dateParts = dateStamp.split('T')[0].split('-');\n const year = parseInt(dateParts[0], 10);\n const month = parseInt(dateParts[1], 10) - 1; // Month is 0-indexed\n const day = parseInt(dateParts[2], 10);\n\n // Create dates using local timezone\n const eventDate = new Date(year, month, day);\n const currentDate = new Date();\n currentDate.setHours(0, 0, 0, 0);\n\n const weekFromNow = new Date();\n weekFromNow.setDate(currentDate.getDate() + 7);\n weekFromNow.setHours(0, 0, 0, 0);\n\n // Check if it's today\n if (\n eventDate.getFullYear() === currentDate.getFullYear() &&\n eventDate.getMonth() === currentDate.getMonth() &&\n eventDate.getDate() === currentDate.getDate()\n ) {\n return 'Today';\n }\n\n // Check if it's within the next 7 days\n if (\n eventDate.getTime() > currentDate.getTime() &&\n eventDate.getTime() <= weekFromNow.getTime()\n ) {\n const days = [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ];\n return days[eventDate.getDay()];\n }\n\n // Otherwise return day of week, month and day\n const days = [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ];\n const months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n ];\n return `${days[eventDate.getDay()]}, ${\n months[eventDate.getMonth()]\n } ${eventDate.getDate()}`;\n};\n\n/**\n * Parse a date string to local Date object\n */\nexport const parseLocalDate = (dateString: string): Date => {\n const parts = dateString.split('-');\n const date = new Date(\n parseInt(parts[0], 10),\n parseInt(parts[1], 10) - 1,\n parseInt(parts[2], 10),\n );\n date.setHours(0, 0, 0, 0);\n return date;\n};\n\n/**\n * Get the start date of an event\n */\nexport const getEventStartDate = (event: EventEntry): Date => {\n return parseLocalDate(event.startStamp.split('T')[0]);\n};\n\n/**\n * Check if an event spans multiple days\n */\nexport const isMultiDayEvent = (event: EventEntry): boolean => {\n const startParts = event.startStamp.split('T')[0].split('-');\n const startMonth = startParts[1];\n const startDay = startParts[2];\n const endMonth = MONTH_MAP[event.endMonth || ''];\n const endDay = (event.endDay || '').padStart(2, '0');\n\n return !(startMonth === endMonth && startDay === endDay);\n};\n\n/**\n * Check if an event starts on a specific date\n */\nexport const eventStartsOnDate = (event: EventEntry, targetDate: Date): boolean => {\n const eventStartDate = getEventStartDate(event);\n return eventStartDate.getTime() === targetDate.getTime();\n};\n\n/**\n * Get event priority for sorting within a date group\n *\n * Priority:\n * 1. Multi-day events that start on this date\n * 2. Single-day events\n * 3. Multi-day events that don't start on this date\n */\nexport const getEventPriority = (event: EventEntry, groupDate: Date): number => {\n const isMulti = isMultiDayEvent(event);\n const startsOnDate = eventStartsOnDate(event, groupDate);\n\n if (isMulti && startsOnDate) return 1;\n if (!isMulti) return 2;\n return 3;\n};\n\n/**\n * Sort events by priority within a date group\n */\nexport const sortEventsByPriority = (\n events: EventEntry[],\n groupDate: Date,\n): EventEntry[] => {\n return [...events].sort((a, b) => {\n const aPriority = getEventPriority(a, groupDate);\n const bPriority = getEventPriority(b, groupDate);\n return aPriority - bPriority;\n });\n};\n\n/**\n * Group events by date\n *\n * Groups events by their start date, with past events grouped under today.\n * Each group is sorted by priority (multi-day events starting today first).\n */\nexport const groupEventsByDate = (events: EventEntry[]): GroupedEvent[] => {\n const currentDate = new Date();\n currentDate.setHours(0, 0, 0, 0);\n\n const grouped = events.reduce((acc, event) => {\n const eventDate = getEventStartDate(event);\n const dateKey =\n eventDate < currentDate\n ? currentDate.toISOString().split('T')[0]\n : event.startStamp;\n\n if (!acc[dateKey]) {\n acc[dateKey] = {\n date: getDateBanner(dateKey),\n events: [],\n };\n }\n acc[dateKey].events.push(event);\n return acc;\n }, {} as Record<string, GroupedEvent>);\n\n // Sort events within each group by priority\n Object.keys(grouped).forEach((dateKey) => {\n const groupDate = parseLocalDate(dateKey);\n grouped[dateKey].events = sortEventsByPriority(\n grouped[dateKey].events,\n groupDate,\n );\n });\n\n // Return groups sorted by date\n return Object.values(grouped).sort((a, b) => {\n const dateA = new Date(\n Object.keys(grouped).find((key) => grouped[key] === a) || '',\n );\n const dateB = new Date(\n Object.keys(grouped).find((key) => grouped[key] === b) || '',\n );\n return dateA.getTime() - dateB.getTime();\n });\n};\n"],"names":["days"],"mappings":"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"}
@@ -1,16 +0,0 @@
1
- import * as Styles from "@universityofmaryland/web-styles-library";
2
- const setShadowStyles = async ({
3
- shadowRoot,
4
- styles
5
- }) => {
6
- const styleElement = document.createElement("style");
7
- const optimizedCss = await Styles.utilities.transform.css.removeDuplicates(
8
- styles
9
- );
10
- styleElement.textContent = optimizedCss;
11
- shadowRoot.appendChild(styleElement);
12
- };
13
- export {
14
- setShadowStyles
15
- };
16
- //# sourceMappingURL=shadow.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"shadow.mjs","sources":["../../../source/helpers/styles/shadow.ts"],"sourcesContent":["/**\n * Shadow DOM Styles Utility\n *\n * Utilities for managing styles within Shadow DOM contexts.\n *\n * @module utilities/styles/shadow\n */\n\nimport * as Styles from '@universityofmaryland/web-styles-library';\n\n/**\n * Sets optimized styles on a shadow root\n *\n * This function takes a CSS string, removes duplicate rules for optimization,\n * and injects it into a shadow root via a style element.\n *\n * @param shadowRoot - The shadow root to inject styles into\n * @param styles - CSS string to optimize and inject\n *\n * @example\n * ```typescript\n * const shadowRoot = element.attachShadow({ mode: 'open' });\n * await setShadowStyles({\n * shadowRoot,\n * styles: '.my-class { color: red; }'\n * });\n * ```\n */\nexport const setShadowStyles = async ({\n shadowRoot,\n styles,\n}: {\n shadowRoot: ShadowRoot;\n styles: string;\n}): Promise<void> => {\n const styleElement = document.createElement('style');\n const optimizedCss = await Styles.utilities.transform.css.removeDuplicates(\n styles,\n );\n styleElement.textContent = optimizedCss;\n shadowRoot.appendChild(styleElement);\n};\n\n/**\n * Updates shadow root styles by replacing or appending\n *\n * This function can either replace all existing styles or append new styles\n * to an existing shadow root.\n *\n * @param shadowRoot - The shadow root to update\n * @param styles - CSS string to optimize and inject\n * @param replace - If true, removes all existing style elements first\n *\n * @example\n * ```typescript\n * // Append additional styles\n * await updateShadowStyles({\n * shadowRoot,\n * styles: '.new-class { color: blue; }',\n * replace: false\n * });\n *\n * // Replace all styles\n * await updateShadowStyles({\n * shadowRoot,\n * styles: '.only-class { color: green; }',\n * replace: true\n * });\n * ```\n */\nexport const updateShadowStyles = async ({\n shadowRoot,\n styles,\n replace = false,\n}: {\n shadowRoot: ShadowRoot;\n styles: string;\n replace?: boolean;\n}): Promise<void> => {\n if (replace) {\n // Remove all existing style elements\n const existingStyles = shadowRoot.querySelectorAll('style');\n existingStyles.forEach((style) => style.remove());\n }\n\n await setShadowStyles({ shadowRoot, styles });\n};\n"],"names":[],"mappings":";AA4BO,MAAM,kBAAkB,OAAO;AAAA,EACpC;AAAA,EACA;AACF,MAGqB;AACnB,QAAM,eAAe,SAAS,cAAc,OAAO;AACnD,QAAM,eAAe,MAAM,OAAO,UAAU,UAAU,IAAI;AAAA,IACxD;AAAA,EAAA;AAEF,eAAa,cAAc;AAC3B,aAAW,YAAY,YAAY;AACrC;"}
package/dist/index.mjs DELETED
@@ -1,11 +0,0 @@
1
- import * as academic from "./academic.mjs";
2
- import * as experts from "./experts.mjs";
3
- import * as events from "./events.mjs";
4
- import * as news from "./news.mjs";
5
- export {
6
- academic,
7
- events,
8
- experts,
9
- news
10
- };
11
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
package/dist/news.mjs DELETED
@@ -1,9 +0,0 @@
1
- import { default as default2 } from "./feeds/news/featured.mjs";
2
- import { default as default3 } from "./feeds/news/grid.mjs";
3
- import { default as default4 } from "./feeds/news/list.mjs";
4
- export {
5
- default2 as featured,
6
- default3 as grid,
7
- default4 as list
8
- };
9
- //# sourceMappingURL=news.mjs.map
package/dist/news.mjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"news.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -1,12 +0,0 @@
1
- var FeedStateEvent = /* @__PURE__ */ ((FeedStateEvent2) => {
2
- FeedStateEvent2["LOADING_START"] = "feed:loading:start";
3
- FeedStateEvent2["LOADING_END"] = "feed:loading:end";
4
- FeedStateEvent2["EMPTY_SHOWN"] = "feed:empty:shown";
5
- FeedStateEvent2["PAGINATION_LOADED"] = "feed:pagination:loaded";
6
- FeedStateEvent2["ANNOUNCEMENT"] = "feed:announcement";
7
- return FeedStateEvent2;
8
- })(FeedStateEvent || {});
9
- export {
10
- FeedStateEvent
11
- };
12
- //# sourceMappingURL=_types.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_types.mjs","sources":["../../source/states/_types.ts"],"sourcesContent":["import { type ElementModel } from '../_types';\n\n/**\n * Configuration for loading state\n */\nexport interface LoadingStateConfig {\n isThemeDark?: boolean;\n}\n\n/**\n * Configuration for empty/no results state\n */\nexport interface EmptyStateConfig {\n message?: string;\n linkUrl?: string;\n linkText?: string;\n isThemeDark?: boolean;\n isAlignedCenter?: boolean;\n}\n\n/**\n * Configuration for pagination state\n */\nexport interface PaginationStateConfig {\n callback: () => void;\n isThemeDark?: boolean;\n isLazyLoad: boolean;\n totalEntries: number | null;\n offset: number;\n}\n\n/**\n * Configuration for announcer (aria-live)\n */\nexport interface AnnouncerConfig {\n message: string;\n politeness?: 'polite' | 'assertive';\n}\n\n/**\n * Feed state events for custom event dispatching\n */\nexport enum FeedStateEvent {\n LOADING_START = 'feed:loading:start',\n LOADING_END = 'feed:loading:end',\n EMPTY_SHOWN = 'feed:empty:shown',\n PAGINATION_LOADED = 'feed:pagination:loaded',\n ANNOUNCEMENT = 'feed:announcement',\n}\n\n/**\n * Standard feed state interface\n */\nexport interface FeedState {\n element: HTMLElement;\n styles: string;\n show?: (container: HTMLElement) => void;\n hide?: () => void;\n destroy?: () => void;\n}\n\n/**\n * Legacy loader API (backwards compatible)\n */\nexport interface LoaderLegacyAPI {\n create: (config: LoadingStateConfig) => ElementModel;\n display: (props: { container: HTMLElement; isThemeDark?: boolean }) => void;\n remove: (props: { container: HTMLElement }) => void;\n}\n\n/**\n * Legacy announcer API (backwards compatible)\n */\nexport interface AnnouncerLegacyAPI {\n create: (config: AnnouncerConfig) => HTMLElement;\n update: (props: { container: HTMLElement; message: string }) => void;\n}\n\n/**\n * Legacy pagination API (backwards compatible)\n */\nexport interface PaginationLegacyAPI {\n create: (config: PaginationStateConfig) => ElementModel | undefined;\n remove: (props: { container: HTMLElement }) => void;\n}\n"],"names":["FeedStateEvent"],"mappings":"AA0CO,IAAK,mCAAAA,oBAAL;AACLA,kBAAA,eAAA,IAAgB;AAChBA,kBAAA,aAAA,IAAc;AACdA,kBAAA,aAAA,IAAc;AACdA,kBAAA,mBAAA,IAAoB;AACpBA,kBAAA,cAAA,IAAe;AALL,SAAAA;AAAA,GAAA,kBAAA,CAAA,CAAA;"}
@@ -1,62 +0,0 @@
1
- import { FeedStateEvent } from "./_types.mjs";
2
- class Announcer {
3
- constructor(config = {}) {
4
- this.politeness = config.politeness || "polite";
5
- this.element = document.createElement("div");
6
- this.element.setAttribute("aria-live", this.politeness);
7
- this.element.setAttribute("role", "status");
8
- this.element.classList.add("sr-only");
9
- this.textElement = document.createElement("p");
10
- this.element.appendChild(this.textElement);
11
- if (config.message) {
12
- this.announce(config.message);
13
- }
14
- }
15
- /**
16
- * Announce a message to screen readers
17
- */
18
- announce(message) {
19
- this.textElement.textContent = message;
20
- this.element.dispatchEvent(
21
- new CustomEvent(FeedStateEvent.ANNOUNCEMENT, {
22
- bubbles: true,
23
- detail: {
24
- message,
25
- politeness: this.politeness,
26
- timestamp: Date.now()
27
- }
28
- })
29
- );
30
- }
31
- /**
32
- * Clear the announcement
33
- */
34
- clear() {
35
- this.textElement.textContent = "";
36
- }
37
- /**
38
- * Change the politeness level
39
- */
40
- setPoliteness(politeness) {
41
- this.politeness = politeness;
42
- this.element.setAttribute("aria-live", politeness);
43
- }
44
- /**
45
- * Remove the announcer from DOM
46
- */
47
- destroy() {
48
- if (this.element.parentNode) {
49
- this.element.remove();
50
- }
51
- }
52
- /**
53
- * Get the announcer element
54
- */
55
- getElement() {
56
- return this.element;
57
- }
58
- }
59
- export {
60
- Announcer
61
- };
62
- //# sourceMappingURL=announcer.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"announcer.mjs","sources":["../../source/states/announcer.ts"],"sourcesContent":["import { type AnnouncerConfig, type AnnouncerLegacyAPI, FeedStateEvent } from './_types';\n\n/**\n * Creates an ARIA live region for screen reader announcements\n *\n * @param config - Announcer configuration\n * @returns HTMLElement configured as an ARIA live region\n *\n * @example\n * ```typescript\n * const announcer = createAnnouncerElement({\n * message: 'Loading complete, 10 items found',\n * politeness: 'polite'\n * });\n * document.body.appendChild(announcer);\n * ```\n */\nexport function createAnnouncerElement(config: AnnouncerConfig): HTMLElement {\n const { message, politeness = 'polite' } = config;\n\n const container = document.createElement('div');\n const textElement = document.createElement('p');\n\n container.setAttribute('aria-live', politeness);\n container.setAttribute('role', 'status');\n container.classList.add('sr-only');\n\n // Use textContent for security instead of innerHTML\n textElement.textContent = message;\n\n container.appendChild(textElement);\n\n return container;\n}\n\n/**\n * Announcer class for managing ARIA live regions\n *\n * Provides a reusable way to announce dynamic content changes to screen readers.\n *\n * @example\n * ```typescript\n * const announcer = new Announcer({ politeness: 'polite' });\n * document.body.appendChild(announcer.element);\n *\n * // Later, announce something\n * announcer.announce('20 new items loaded');\n *\n * // Cleanup when done\n * announcer.destroy();\n * ```\n */\nexport class Announcer {\n private element: HTMLElement;\n private textElement: HTMLElement;\n private politeness: 'polite' | 'assertive';\n\n constructor(config: Partial<AnnouncerConfig> = {}) {\n this.politeness = config.politeness || 'polite';\n\n // Create the live region\n this.element = document.createElement('div');\n this.element.setAttribute('aria-live', this.politeness);\n this.element.setAttribute('role', 'status');\n this.element.classList.add('sr-only');\n\n // Create text container\n this.textElement = document.createElement('p');\n this.element.appendChild(this.textElement);\n\n // Set initial message if provided\n if (config.message) {\n this.announce(config.message);\n }\n }\n\n /**\n * Announce a message to screen readers\n */\n announce(message: string): void {\n // Use textContent for security\n this.textElement.textContent = message;\n\n // Dispatch custom event for tracking\n this.element.dispatchEvent(\n new CustomEvent(FeedStateEvent.ANNOUNCEMENT, {\n bubbles: true,\n detail: {\n message,\n politeness: this.politeness,\n timestamp: Date.now(),\n },\n })\n );\n }\n\n /**\n * Clear the announcement\n */\n clear(): void {\n this.textElement.textContent = '';\n }\n\n /**\n * Change the politeness level\n */\n setPoliteness(politeness: 'polite' | 'assertive'): void {\n this.politeness = politeness;\n this.element.setAttribute('aria-live', politeness);\n }\n\n /**\n * Remove the announcer from DOM\n */\n destroy(): void {\n if (this.element.parentNode) {\n this.element.remove();\n }\n }\n\n /**\n * Get the announcer element\n */\n getElement(): HTMLElement {\n return this.element;\n }\n}\n\n// =============================================================================\n// Backwards Compatible Exports (Legacy API)\n// =============================================================================\n\n/**\n * @deprecated Use Announcer class or createAnnouncerElement instead\n */\nconst create = (config: AnnouncerConfig): HTMLElement => {\n return createAnnouncerElement(config);\n};\n\n/**\n * @deprecated Use Announcer.announce() instead\n */\nconst update = ({\n container,\n message,\n}: {\n container: HTMLElement;\n message: string;\n}): void => {\n const element = container.querySelector(`[aria-live]`) as HTMLDivElement;\n const textElement = element?.querySelector('p');\n\n if (textElement) {\n textElement.textContent = message; // Use textContent for security\n }\n};\n\n/**\n * Legacy API for backwards compatibility\n * @deprecated Use Announcer class instead\n */\nexport default {\n create,\n update,\n} as AnnouncerLegacyAPI;\n"],"names":[],"mappings":";AAoDO,MAAM,UAAU;AAAA,EAKrB,YAAY,SAAmC,IAAI;AACjD,SAAK,aAAa,OAAO,cAAc;AAGvC,SAAK,UAAU,SAAS,cAAc,KAAK;AAC3C,SAAK,QAAQ,aAAa,aAAa,KAAK,UAAU;AACtD,SAAK,QAAQ,aAAa,QAAQ,QAAQ;AAC1C,SAAK,QAAQ,UAAU,IAAI,SAAS;AAGpC,SAAK,cAAc,SAAS,cAAc,GAAG;AAC7C,SAAK,QAAQ,YAAY,KAAK,WAAW;AAGzC,QAAI,OAAO,SAAS;AAClB,WAAK,SAAS,OAAO,OAAO;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,SAAuB;AAE9B,SAAK,YAAY,cAAc;AAG/B,SAAK,QAAQ;AAAA,MACX,IAAI,YAAY,eAAe,cAAc;AAAA,QAC3C,SAAS;AAAA,QACT,QAAQ;AAAA,UACN;AAAA,UACA,YAAY,KAAK;AAAA,UACjB,WAAW,KAAK,IAAA;AAAA,QAAI;AAAA,MACtB,CACD;AAAA,IAAA;AAAA,EAEL;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,YAAY,cAAc;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,YAA0C;AACtD,SAAK,aAAa;AAClB,SAAK,QAAQ,aAAa,aAAa,UAAU;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,QAAI,KAAK,QAAQ,YAAY;AAC3B,WAAK,QAAQ,OAAA;AAAA,IACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAA0B;AACxB,WAAO,KAAK;AAAA,EACd;AACF;"}
@@ -1,104 +0,0 @@
1
- import { ElementBuilder } from "@universityofmaryland/web-builder-library";
2
- import { Atomic } from "@universityofmaryland/web-elements-library";
3
- import * as Styles from "@universityofmaryland/web-styles-library";
4
- import { theme } from "@universityofmaryland/web-utilities-library/theme";
5
- import { FeedStateEvent } from "./_types.mjs";
6
- function createEmptyElement(config = {}) {
7
- const {
8
- message = "No results found",
9
- linkUrl,
10
- linkText,
11
- isThemeDark = false,
12
- isAlignedCenter = true
13
- } = config;
14
- const headline = new ElementBuilder(document.createElement("p")).styled(Styles.typography.sans.compose("extralarge")).withStyles({
15
- element: {
16
- textTransform: "uppercase",
17
- color: theme.foreground(isThemeDark)
18
- }
19
- }).build();
20
- headline.element.textContent = message;
21
- const composite = new ElementBuilder().styled(Styles.layout.grid.stacked).withChild(headline).withStyles({
22
- element: {
23
- [`& *`]: {
24
- textAlign: isAlignedCenter ? "center" : "left"
25
- },
26
- [`& *:not(:first-child)`]: {
27
- marginTop: `${Styles.token.spacing.md}`
28
- }
29
- }
30
- });
31
- if (linkUrl && linkText) {
32
- const link = document.createElement("a");
33
- link.textContent = linkText;
34
- link.setAttribute("href", linkUrl);
35
- link.setAttribute("target", "_blank");
36
- link.setAttribute("rel", "noopener noreferrer");
37
- const ctaButton = Atomic.actions.options({
38
- element: link,
39
- isTypeOutline: true,
40
- isThemeDark
41
- });
42
- composite.withChild(ctaButton);
43
- }
44
- return composite.build();
45
- }
46
- class EmptyState {
47
- constructor(config = {}) {
48
- this.container = null;
49
- this.config = config;
50
- this.model = createEmptyElement(config);
51
- }
52
- /**
53
- * Render the empty state in a container
54
- */
55
- render(container) {
56
- this.container = container;
57
- container.appendChild(this.model.element);
58
- container.dispatchEvent(
59
- new CustomEvent(FeedStateEvent.EMPTY_SHOWN, {
60
- bubbles: true,
61
- detail: {
62
- message: this.config.message,
63
- timestamp: Date.now()
64
- }
65
- })
66
- );
67
- }
68
- /**
69
- * Update the empty state message
70
- */
71
- updateMessage(message) {
72
- const headline = this.model.element.querySelector("p");
73
- if (headline) {
74
- headline.textContent = message;
75
- this.config.message = message;
76
- }
77
- }
78
- /**
79
- * Remove the empty state from DOM
80
- */
81
- destroy() {
82
- if (this.model.element.parentNode) {
83
- this.model.element.remove();
84
- }
85
- this.container = null;
86
- }
87
- /**
88
- * Get the empty state element
89
- */
90
- get element() {
91
- return this.model.element;
92
- }
93
- /**
94
- * Get the empty state styles
95
- */
96
- get styles() {
97
- return this.model.styles;
98
- }
99
- }
100
- export {
101
- EmptyState,
102
- createEmptyElement
103
- };
104
- //# sourceMappingURL=empty.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"empty.mjs","sources":["../../source/states/empty.ts"],"sourcesContent":["import { ElementBuilder } from '@universityofmaryland/web-builder-library';\nimport { Atomic } from '@universityofmaryland/web-elements-library';\nimport * as Styles from '@universityofmaryland/web-styles-library';\nimport { theme } from '@universityofmaryland/web-utilities-library/theme';\n\nimport { type ElementModel } from '../_types';\nimport { type EmptyStateConfig, FeedStateEvent } from './_types';\n\n/**\n * Creates an empty/no results state element\n *\n * @param config - Empty state configuration\n * @returns ElementModel with empty state UI\n *\n * @example\n * ```typescript\n * const empty = createEmptyElement({\n * message: 'No events found',\n * linkUrl: 'https://calendar.umd.edu',\n * linkText: 'View all events'\n * });\n * container.appendChild(empty.element);\n * ```\n */\nexport function createEmptyElement(config: EmptyStateConfig = {}): ElementModel {\n const {\n message = 'No results found',\n linkUrl,\n linkText,\n isThemeDark = false,\n isAlignedCenter = true,\n } = config;\n\n const headline = new ElementBuilder(document.createElement('p'))\n .styled(Styles.typography.sans.compose('extralarge'))\n .withStyles({\n element: {\n textTransform: 'uppercase',\n color: theme.foreground(isThemeDark),\n },\n })\n .build();\n\n // Use textContent for security instead of innerHTML\n headline.element.textContent = message;\n\n const composite = new ElementBuilder()\n .styled(Styles.layout.grid.stacked)\n .withChild(headline)\n .withStyles({\n element: {\n [`& *`]: {\n textAlign: isAlignedCenter ? 'center' : 'left',\n },\n [`& *:not(:first-child)`]: {\n marginTop: `${Styles.token.spacing.md}`,\n },\n },\n });\n\n // Add optional CTA link\n if (linkUrl && linkText) {\n const link = document.createElement('a');\n link.textContent = linkText; // Use textContent for security\n link.setAttribute('href', linkUrl);\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n\n const ctaButton = Atomic.actions.options({\n element: link,\n isTypeOutline: true,\n isThemeDark,\n });\n\n composite.withChild(ctaButton);\n }\n\n return composite.build();\n}\n\n/**\n * Empty state manager class\n *\n * Manages the display of empty/no results state with optional CTA.\n *\n * @example\n * ```typescript\n * const empty = new EmptyState({\n * message: 'No articles found',\n * linkUrl: 'https://today.umd.edu',\n * linkText: 'View all articles'\n * });\n * empty.render(container);\n * ```\n */\nexport class EmptyState {\n private model: ElementModel;\n private container: HTMLElement | null = null;\n private config: EmptyStateConfig;\n\n constructor(config: EmptyStateConfig = {}) {\n this.config = config;\n this.model = createEmptyElement(config);\n }\n\n /**\n * Render the empty state in a container\n */\n render(container: HTMLElement): void {\n this.container = container;\n container.appendChild(this.model.element);\n\n container.dispatchEvent(\n new CustomEvent(FeedStateEvent.EMPTY_SHOWN, {\n bubbles: true,\n detail: {\n message: this.config.message,\n timestamp: Date.now(),\n },\n })\n );\n }\n\n /**\n * Update the empty state message\n */\n updateMessage(message: string): void {\n const headline = this.model.element.querySelector('p');\n if (headline) {\n headline.textContent = message;\n this.config.message = message;\n }\n }\n\n /**\n * Remove the empty state from DOM\n */\n destroy(): void {\n if (this.model.element.parentNode) {\n this.model.element.remove();\n }\n this.container = null;\n }\n\n /**\n * Get the empty state element\n */\n get element(): HTMLElement {\n return this.model.element;\n }\n\n /**\n * Get the empty state styles\n */\n get styles(): string {\n return this.model.styles;\n }\n}\n\n// =============================================================================\n// Backwards Compatible Export (Legacy API)\n// =============================================================================\n\n/**\n * Create empty state element (legacy function API)\n *\n * @deprecated Use EmptyState class or createEmptyElement function instead\n */\nexport default (config: EmptyStateConfig = {}): ElementModel => {\n return createEmptyElement(config);\n};\n"],"names":[],"mappings":";;;;;AAwBO,SAAS,mBAAmB,SAA2B,IAAkB;AAC9E,QAAM;AAAA,IACJ,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,kBAAkB;AAAA,EAAA,IAChB;AAEJ,QAAM,WAAW,IAAI,eAAe,SAAS,cAAc,GAAG,CAAC,EAC5D,OAAO,OAAO,WAAW,KAAK,QAAQ,YAAY,CAAC,EACnD,WAAW;AAAA,IACV,SAAS;AAAA,MACP,eAAe;AAAA,MACf,OAAO,MAAM,WAAW,WAAW;AAAA,IAAA;AAAA,EACrC,CACD,EACA,MAAA;AAGH,WAAS,QAAQ,cAAc;AAE/B,QAAM,YAAY,IAAI,iBACnB,OAAO,OAAO,OAAO,KAAK,OAAO,EACjC,UAAU,QAAQ,EAClB,WAAW;AAAA,IACV,SAAS;AAAA,MACP,CAAC,KAAK,GAAG;AAAA,QACP,WAAW,kBAAkB,WAAW;AAAA,MAAA;AAAA,MAE1C,CAAC,uBAAuB,GAAG;AAAA,QACzB,WAAW,GAAG,OAAO,MAAM,QAAQ,EAAE;AAAA,MAAA;AAAA,IACvC;AAAA,EACF,CACD;AAGH,MAAI,WAAW,UAAU;AACvB,UAAM,OAAO,SAAS,cAAc,GAAG;AACvC,SAAK,cAAc;AACnB,SAAK,aAAa,QAAQ,OAAO;AACjC,SAAK,aAAa,UAAU,QAAQ;AACpC,SAAK,aAAa,OAAO,qBAAqB;AAE9C,UAAM,YAAY,OAAO,QAAQ,QAAQ;AAAA,MACvC,SAAS;AAAA,MACT,eAAe;AAAA,MACf;AAAA,IAAA,CACD;AAED,cAAU,UAAU,SAAS;AAAA,EAC/B;AAEA,SAAO,UAAU,MAAA;AACnB;AAiBO,MAAM,WAAW;AAAA,EAKtB,YAAY,SAA2B,IAAI;AAH3C,SAAQ,YAAgC;AAItC,SAAK,SAAS;AACd,SAAK,QAAQ,mBAAmB,MAAM;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,WAA8B;AACnC,SAAK,YAAY;AACjB,cAAU,YAAY,KAAK,MAAM,OAAO;AAExC,cAAU;AAAA,MACR,IAAI,YAAY,eAAe,aAAa;AAAA,QAC1C,SAAS;AAAA,QACT,QAAQ;AAAA,UACN,SAAS,KAAK,OAAO;AAAA,UACrB,WAAW,KAAK,IAAA;AAAA,QAAI;AAAA,MACtB,CACD;AAAA,IAAA;AAAA,EAEL;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,SAAuB;AACnC,UAAM,WAAW,KAAK,MAAM,QAAQ,cAAc,GAAG;AACrD,QAAI,UAAU;AACZ,eAAS,cAAc;AACvB,WAAK,OAAO,UAAU;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,QAAI,KAAK,MAAM,QAAQ,YAAY;AACjC,WAAK,MAAM,QAAQ,OAAA;AAAA,IACrB;AACA,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,UAAuB;AACzB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAiB;AACnB,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;"}
@@ -1,155 +0,0 @@
1
- import { ElementBuilder } from "@universityofmaryland/web-builder-library";
2
- import { token } from "@universityofmaryland/web-styles-library";
3
- import { FeedStateEvent } from "./_types.mjs";
4
- const ID_UMD_LOADER = "umd-loader-container";
5
- const keyframes = `
6
- @keyframes loader-first-animation {
7
- 0% {
8
- transform: scale(0);
9
- }
10
- 100% {
11
- transform: scale(1);
12
- }
13
- }
14
-
15
- @keyframes loader-last-animation {
16
- 0% {
17
- transform: scale(1);
18
- }
19
- 100% {
20
- transform: scale(0);
21
- }
22
- }
23
-
24
- @keyframes loader-middle-animation {
25
- 0% {
26
- transform: translate(0, 0);
27
- }
28
- 100% {
29
- transform: translate(24px, 0);
30
- }
31
- }
32
- `;
33
- function createLoadingElement(config = {}) {
34
- const { isThemeDark = false } = config;
35
- const defaultDotStyles = {
36
- position: "absolute",
37
- top: "50%",
38
- transform: "translateY(-50%)",
39
- width: "8px",
40
- height: "8px",
41
- borderRadius: "50%",
42
- background: isThemeDark ? token.color.gray.light : token.color.gray.dark,
43
- animationTimingFunction: "cubic-bezier(0, 1, 1, 0)"
44
- };
45
- const innerElmOne = new ElementBuilder().withClassName(`${ID_UMD_LOADER}-one`).withStyles({
46
- element: {
47
- ...defaultDotStyles,
48
- left: "5px",
49
- animation: "loader-first-animation 0.6s infinite"
50
- }
51
- }).build();
52
- const innerElmTwo = new ElementBuilder().withClassName(`${ID_UMD_LOADER}-two`).withStyles({
53
- element: {
54
- ...defaultDotStyles,
55
- left: "5px",
56
- animation: "loader-middle-animation 0.6s infinite"
57
- }
58
- }).build();
59
- const innerElmThree = new ElementBuilder().withClassName(`${ID_UMD_LOADER}-three`).withStyles({
60
- element: {
61
- ...defaultDotStyles,
62
- left: "24px",
63
- animation: "loader-middle-animation 0.6s infinite"
64
- }
65
- }).build();
66
- const innerElmFour = new ElementBuilder().withClassName(`${ID_UMD_LOADER}-four`).withStyles({
67
- element: {
68
- ...defaultDotStyles,
69
- left: "45px",
70
- animation: "loader-last-animation 0.6s infinite"
71
- }
72
- }).build();
73
- const wrapper = new ElementBuilder().withClassName(`${ID_UMD_LOADER}-wrapper`).withChild(innerElmOne).withChild(innerElmTwo).withChild(innerElmThree).withChild(innerElmFour).withStyles({
74
- element: {
75
- position: "relative"
76
- }
77
- }).build();
78
- const composite = new ElementBuilder().withClassName(ID_UMD_LOADER).withChild(wrapper).withStyles({
79
- element: {
80
- display: "flex",
81
- justifyContent: "center",
82
- alignItems: "center",
83
- padding: "10px 0",
84
- minHeight: "40px",
85
- position: "relative",
86
- gridColumn: "1 / -1"
87
- }
88
- }).build();
89
- composite.styles += keyframes;
90
- return composite;
91
- }
92
- class LoadingState {
93
- constructor(config = {}) {
94
- this.container = null;
95
- this.isVisible = false;
96
- this.model = createLoadingElement(config);
97
- }
98
- /**
99
- * Show the loading spinner in a container
100
- */
101
- show(container) {
102
- if (!this.isVisible) {
103
- this.container = container;
104
- container.appendChild(this.model.element);
105
- this.isVisible = true;
106
- container.dispatchEvent(
107
- new CustomEvent(FeedStateEvent.LOADING_START, {
108
- bubbles: true,
109
- detail: { timestamp: Date.now() }
110
- })
111
- );
112
- }
113
- }
114
- /**
115
- * Hide and remove the loading spinner
116
- */
117
- hide() {
118
- if (this.isVisible && this.model.element.parentNode) {
119
- this.model.element.remove();
120
- this.isVisible = false;
121
- if (this.container) {
122
- this.container.dispatchEvent(
123
- new CustomEvent(FeedStateEvent.LOADING_END, {
124
- bubbles: true,
125
- detail: { timestamp: Date.now() }
126
- })
127
- );
128
- }
129
- }
130
- }
131
- /**
132
- * Cleanup and remove the loading spinner
133
- */
134
- destroy() {
135
- this.hide();
136
- this.container = null;
137
- }
138
- /**
139
- * Get the loading spinner element
140
- */
141
- get element() {
142
- return this.model.element;
143
- }
144
- /**
145
- * Get the loading spinner styles
146
- */
147
- get styles() {
148
- return this.model.styles;
149
- }
150
- }
151
- export {
152
- LoadingState,
153
- createLoadingElement
154
- };
155
- //# sourceMappingURL=loading.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loading.mjs","sources":["../../source/states/loading.ts"],"sourcesContent":["import { ElementBuilder } from '@universityofmaryland/web-builder-library';\nimport { token } from '@universityofmaryland/web-styles-library';\n\nimport { type ElementModel } from '../_types';\nimport {\n type LoadingStateConfig,\n type LoaderLegacyAPI,\n FeedStateEvent,\n} from './_types';\n\nconst ID_UMD_LOADER = 'umd-loader-container';\n\nconst keyframes = `\n @keyframes loader-first-animation {\n 0% {\n transform: scale(0);\n }\n 100% {\n transform: scale(1);\n }\n }\n\n @keyframes loader-last-animation {\n 0% {\n transform: scale(1);\n }\n 100% {\n transform: scale(0);\n }\n }\n\n @keyframes loader-middle-animation {\n 0% {\n transform: translate(0, 0);\n }\n 100% {\n transform: translate(24px, 0);\n }\n }\n`;\n\n/**\n * Creates a loading spinner element model\n *\n * @param config - Loading state configuration\n * @returns ElementModel with loading spinner\n *\n * @example\n * ```typescript\n * const loading = createLoadingElement({ isThemeDark: false });\n * container.appendChild(loading.element);\n * ```\n */\nexport function createLoadingElement(config: LoadingStateConfig = {}): ElementModel {\n const { isThemeDark = false } = config;\n\n const defaultDotStyles = {\n position: 'absolute',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '8px',\n height: '8px',\n borderRadius: '50%',\n background: isThemeDark ? token.color.gray.light : token.color.gray.dark,\n animationTimingFunction: 'cubic-bezier(0, 1, 1, 0)',\n };\n\n const innerElmOne = new ElementBuilder()\n .withClassName(`${ID_UMD_LOADER}-one`)\n .withStyles({\n element: {\n ...defaultDotStyles,\n left: '5px',\n animation: 'loader-first-animation 0.6s infinite',\n },\n })\n .build();\n\n const innerElmTwo = new ElementBuilder()\n .withClassName(`${ID_UMD_LOADER}-two`)\n .withStyles({\n element: {\n ...defaultDotStyles,\n left: '5px',\n animation: 'loader-middle-animation 0.6s infinite',\n },\n })\n .build();\n\n const innerElmThree = new ElementBuilder()\n .withClassName(`${ID_UMD_LOADER}-three`)\n .withStyles({\n element: {\n ...defaultDotStyles,\n left: '24px',\n animation: 'loader-middle-animation 0.6s infinite',\n },\n })\n .build();\n\n const innerElmFour = new ElementBuilder()\n .withClassName(`${ID_UMD_LOADER}-four`)\n .withStyles({\n element: {\n ...defaultDotStyles,\n left: '45px',\n animation: 'loader-last-animation 0.6s infinite',\n },\n })\n .build();\n\n const wrapper = new ElementBuilder()\n .withClassName(`${ID_UMD_LOADER}-wrapper`)\n .withChild(innerElmOne)\n .withChild(innerElmTwo)\n .withChild(innerElmThree)\n .withChild(innerElmFour)\n .withStyles({\n element: {\n position: 'relative',\n },\n })\n .build();\n\n const composite = new ElementBuilder()\n .withClassName(ID_UMD_LOADER)\n .withChild(wrapper)\n .withStyles({\n element: {\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '10px 0',\n minHeight: '40px',\n position: 'relative',\n gridColumn: '1 / -1',\n },\n })\n .build();\n\n composite.styles += keyframes;\n\n return composite;\n}\n\n/**\n * Loading state manager class\n *\n * Manages the lifecycle of a loading spinner with show/hide functionality.\n *\n * @example\n * ```typescript\n * const loading = new LoadingState({ isThemeDark: false });\n * loading.show(container);\n * // ... async operation\n * loading.hide();\n * ```\n */\nexport class LoadingState {\n private model: ElementModel;\n private container: HTMLElement | null = null;\n private isVisible: boolean = false;\n\n constructor(config: LoadingStateConfig = {}) {\n this.model = createLoadingElement(config);\n }\n\n /**\n * Show the loading spinner in a container\n */\n show(container: HTMLElement): void {\n if (!this.isVisible) {\n this.container = container;\n container.appendChild(this.model.element);\n this.isVisible = true;\n\n container.dispatchEvent(\n new CustomEvent(FeedStateEvent.LOADING_START, {\n bubbles: true,\n detail: { timestamp: Date.now() },\n })\n );\n }\n }\n\n /**\n * Hide and remove the loading spinner\n */\n hide(): void {\n if (this.isVisible && this.model.element.parentNode) {\n this.model.element.remove();\n this.isVisible = false;\n\n if (this.container) {\n this.container.dispatchEvent(\n new CustomEvent(FeedStateEvent.LOADING_END, {\n bubbles: true,\n detail: { timestamp: Date.now() },\n })\n );\n }\n }\n }\n\n /**\n * Cleanup and remove the loading spinner\n */\n destroy(): void {\n this.hide();\n this.container = null;\n }\n\n /**\n * Get the loading spinner element\n */\n get element(): HTMLElement {\n return this.model.element;\n }\n\n /**\n * Get the loading spinner styles\n */\n get styles(): string {\n return this.model.styles;\n }\n}\n\n// =============================================================================\n// Backwards Compatible Exports (Legacy API)\n// =============================================================================\n\n/**\n * @deprecated Use LoadingState class instead\n */\nconst create = (config: LoadingStateConfig = {}): ElementModel => {\n return createLoadingElement(config);\n};\n\n/**\n * @deprecated Use LoadingState.show() instead\n */\nconst display = ({\n container,\n isThemeDark,\n}: {\n container: HTMLElement;\n isThemeDark?: boolean;\n}): void => {\n const loading = createLoadingElement({ isThemeDark });\n container.appendChild(loading.element);\n};\n\n/**\n * @deprecated Use LoadingState.hide() instead\n */\nconst remove = ({ container }: { container: HTMLElement }): void => {\n const loader = container.querySelector(`.${ID_UMD_LOADER}`) as HTMLDivElement;\n if (loader) loader.remove();\n};\n\n/**\n * Legacy API for backwards compatibility\n * @deprecated Use LoadingState class instead\n */\nexport default {\n create,\n display,\n remove,\n} as LoaderLegacyAPI;\n"],"names":[],"mappings":";;;AAUA,MAAM,gBAAgB;AAEtB,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyCX,SAAS,qBAAqB,SAA6B,IAAkB;AAClF,QAAM,EAAE,cAAc,MAAA,IAAU;AAEhC,QAAM,mBAAmB;AAAA,IACvB,UAAU;AAAA,IACV,KAAK;AAAA,IACL,WAAW;AAAA,IACX,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,YAAY,cAAc,MAAM,MAAM,KAAK,QAAQ,MAAM,MAAM,KAAK;AAAA,IACpE,yBAAyB;AAAA,EAAA;AAG3B,QAAM,cAAc,IAAI,eAAA,EACrB,cAAc,GAAG,aAAa,MAAM,EACpC,WAAW;AAAA,IACV,SAAS;AAAA,MACP,GAAG;AAAA,MACH,MAAM;AAAA,MACN,WAAW;AAAA,IAAA;AAAA,EACb,CACD,EACA,MAAA;AAEH,QAAM,cAAc,IAAI,eAAA,EACrB,cAAc,GAAG,aAAa,MAAM,EACpC,WAAW;AAAA,IACV,SAAS;AAAA,MACP,GAAG;AAAA,MACH,MAAM;AAAA,MACN,WAAW;AAAA,IAAA;AAAA,EACb,CACD,EACA,MAAA;AAEH,QAAM,gBAAgB,IAAI,eAAA,EACvB,cAAc,GAAG,aAAa,QAAQ,EACtC,WAAW;AAAA,IACV,SAAS;AAAA,MACP,GAAG;AAAA,MACH,MAAM;AAAA,MACN,WAAW;AAAA,IAAA;AAAA,EACb,CACD,EACA,MAAA;AAEH,QAAM,eAAe,IAAI,eAAA,EACtB,cAAc,GAAG,aAAa,OAAO,EACrC,WAAW;AAAA,IACV,SAAS;AAAA,MACP,GAAG;AAAA,MACH,MAAM;AAAA,MACN,WAAW;AAAA,IAAA;AAAA,EACb,CACD,EACA,MAAA;AAEH,QAAM,UAAU,IAAI,iBACjB,cAAc,GAAG,aAAa,UAAU,EACxC,UAAU,WAAW,EACrB,UAAU,WAAW,EACrB,UAAU,aAAa,EACvB,UAAU,YAAY,EACtB,WAAW;AAAA,IACV,SAAS;AAAA,MACP,UAAU;AAAA,IAAA;AAAA,EACZ,CACD,EACA,MAAA;AAEH,QAAM,YAAY,IAAI,eAAA,EACnB,cAAc,aAAa,EAC3B,UAAU,OAAO,EACjB,WAAW;AAAA,IACV,SAAS;AAAA,MACP,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,WAAW;AAAA,MACX,UAAU;AAAA,MACV,YAAY;AAAA,IAAA;AAAA,EACd,CACD,EACA,MAAA;AAEH,YAAU,UAAU;AAEpB,SAAO;AACT;AAeO,MAAM,aAAa;AAAA,EAKxB,YAAY,SAA6B,IAAI;AAH7C,SAAQ,YAAgC;AACxC,SAAQ,YAAqB;AAG3B,SAAK,QAAQ,qBAAqB,MAAM;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,WAA8B;AACjC,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY;AACjB,gBAAU,YAAY,KAAK,MAAM,OAAO;AACxC,WAAK,YAAY;AAEjB,gBAAU;AAAA,QACR,IAAI,YAAY,eAAe,eAAe;AAAA,UAC5C,SAAS;AAAA,UACT,QAAQ,EAAE,WAAW,KAAK,MAAI;AAAA,QAAE,CACjC;AAAA,MAAA;AAAA,IAEL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACX,QAAI,KAAK,aAAa,KAAK,MAAM,QAAQ,YAAY;AACnD,WAAK,MAAM,QAAQ,OAAA;AACnB,WAAK,YAAY;AAEjB,UAAI,KAAK,WAAW;AAClB,aAAK,UAAU;AAAA,UACb,IAAI,YAAY,eAAe,aAAa;AAAA,YAC1C,SAAS;AAAA,YACT,QAAQ,EAAE,WAAW,KAAK,MAAI;AAAA,UAAE,CACjC;AAAA,QAAA;AAAA,MAEL;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,KAAA;AACL,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,UAAuB;AACzB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAiB;AACnB,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;"}