mnfst 0.5.14

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 (47) hide show
  1. package/LICENSE +11 -0
  2. package/README.md +58 -0
  3. package/dist/manifest.accordion.css +81 -0
  4. package/dist/manifest.appwrite.auth.js +6247 -0
  5. package/dist/manifest.appwrite.data.js +1586 -0
  6. package/dist/manifest.appwrite.presence.js +1845 -0
  7. package/dist/manifest.avatar.css +113 -0
  8. package/dist/manifest.button.css +79 -0
  9. package/dist/manifest.checkbox.css +58 -0
  10. package/dist/manifest.code.css +453 -0
  11. package/dist/manifest.code.js +958 -0
  12. package/dist/manifest.code.min.css +1 -0
  13. package/dist/manifest.components.js +737 -0
  14. package/dist/manifest.css +3124 -0
  15. package/dist/manifest.data.js +11413 -0
  16. package/dist/manifest.dialog.css +130 -0
  17. package/dist/manifest.divider.css +77 -0
  18. package/dist/manifest.dropdown.css +278 -0
  19. package/dist/manifest.dropdowns.js +378 -0
  20. package/dist/manifest.form.css +169 -0
  21. package/dist/manifest.icons.js +161 -0
  22. package/dist/manifest.input.css +129 -0
  23. package/dist/manifest.js +302 -0
  24. package/dist/manifest.localization.js +571 -0
  25. package/dist/manifest.markdown.js +738 -0
  26. package/dist/manifest.min.css +1 -0
  27. package/dist/manifest.radio.css +38 -0
  28. package/dist/manifest.resize.css +233 -0
  29. package/dist/manifest.resize.js +442 -0
  30. package/dist/manifest.router.js +1207 -0
  31. package/dist/manifest.sidebar.css +102 -0
  32. package/dist/manifest.slides.css +80 -0
  33. package/dist/manifest.slides.js +173 -0
  34. package/dist/manifest.switch.css +44 -0
  35. package/dist/manifest.table.css +74 -0
  36. package/dist/manifest.tabs.js +273 -0
  37. package/dist/manifest.tailwind.js +578 -0
  38. package/dist/manifest.theme.css +119 -0
  39. package/dist/manifest.themes.js +109 -0
  40. package/dist/manifest.toast.css +92 -0
  41. package/dist/manifest.toasts.js +285 -0
  42. package/dist/manifest.tooltip.css +156 -0
  43. package/dist/manifest.tooltips.js +331 -0
  44. package/dist/manifest.typography.css +341 -0
  45. package/dist/manifest.utilities.css +399 -0
  46. package/dist/manifest.utilities.js +3197 -0
  47. package/package.json +63 -0
@@ -0,0 +1,378 @@
1
+ /* Manifest Dropdowns */
2
+
3
+ // Initialize plugin when either DOM is ready or Alpine is ready
4
+ function initializeDropdownPlugin() {
5
+ // Ensure Alpine.js context exists for directives to work
6
+ function ensureAlpineContext() {
7
+ const body = document.body;
8
+ if (!body.hasAttribute('x-data')) {
9
+ body.setAttribute('x-data', '{}');
10
+ }
11
+ }
12
+
13
+ // Helper to register directives
14
+ function registerDirective(name, handler) {
15
+ Alpine.directive(name, handler);
16
+ }
17
+
18
+ // Check if a menu element is nested (triggered from within another menu)
19
+ const isNestedMenu = (menu) => {
20
+ // Find all buttons/elements that target this menu
21
+ const menuId = menu.id;
22
+ const triggers = document.querySelectorAll(`[popovertarget="${menuId}"], [x-dropdown="${menuId}"], [x-dropdown*="${menuId}"]`);
23
+
24
+ // Check if any trigger is inside another popover menu
25
+ for (const trigger of triggers) {
26
+ let parent = trigger.parentElement;
27
+ while (parent) {
28
+ if (parent.tagName === 'MENU' && parent.hasAttribute('popover')) {
29
+ return true;
30
+ }
31
+ parent = parent.parentElement;
32
+ }
33
+ }
34
+ return false;
35
+ };
36
+
37
+ // Ensure Alpine.js context exists
38
+ ensureAlpineContext();
39
+
40
+ // Register dropdown directive
41
+ registerDirective('dropdown', (el, { modifiers, expression }, { effect, evaluateLater }) => {
42
+ let menu;
43
+
44
+ // Shared hover state for all dropdown types
45
+ let hoverTimeout;
46
+ let autoCloseTimeout;
47
+ let startAutoCloseTimer;
48
+
49
+ effect(() => {
50
+
51
+ // Defer processing to ensure Alpine is fully ready
52
+ setTimeout(() => {
53
+ if (!window.Alpine) {
54
+ console.warn('[Manifest] Alpine not available for dropdown processing');
55
+ return;
56
+ }
57
+
58
+ // Generate a unique anchor code for positioning
59
+ const anchorCode = Math.random().toString(36).substr(2, 9);
60
+
61
+ // Evaluate the expression to get the actual menu ID
62
+ let dropdownId;
63
+ if (expression) {
64
+ // Check if expression contains template literals or is a static string
65
+ if (expression.includes('${') || expression.includes('`')) {
66
+ // Use evaluateLater for dynamic expressions
67
+ const evaluator = evaluateLater(expression);
68
+ evaluator(value => {
69
+ dropdownId = value;
70
+ });
71
+ } else {
72
+ // Static string - use as-is
73
+ dropdownId = expression;
74
+ }
75
+ } else {
76
+ dropdownId = `dropdown-${anchorCode}`;
77
+ }
78
+
79
+ // Check if expression refers to a template ID
80
+ if (dropdownId && document.getElementById(dropdownId)?.tagName === 'TEMPLATE') {
81
+ // Clone template content and generate unique ID
82
+ const template = document.getElementById(dropdownId);
83
+ menu = template.content.cloneNode(true).firstElementChild;
84
+ const uniqueDropdownId = `dropdown-${anchorCode}`;
85
+ menu.setAttribute('id', uniqueDropdownId);
86
+ document.body.appendChild(menu);
87
+ el.setAttribute('popovertarget', uniqueDropdownId);
88
+
89
+ // Initialize Alpine on the cloned menu
90
+ Alpine.initTree(menu);
91
+ } else {
92
+ // Original behavior for static dropdowns
93
+ menu = document.getElementById(dropdownId);
94
+ if (!menu) {
95
+ // Check if this might be a component-based dropdown
96
+ if (window.ManifestComponentsRegistry && window.ManifestComponentsLoader) {
97
+ // Try to find the menu in components
98
+ const componentName = dropdownId; // Assume the dropdownId is the component name
99
+ const registry = window.ManifestComponentsRegistry;
100
+
101
+ if (registry.registered.has(componentName)) {
102
+ // Component exists, wait for it to be loaded
103
+ const waitForComponent = async () => {
104
+ const loader = window.ManifestComponentsLoader;
105
+ const content = await loader.loadComponent(componentName);
106
+ if (content) {
107
+ // Create a temporary container to parse the component
108
+ const tempDiv = document.createElement('div');
109
+ tempDiv.innerHTML = content.trim();
110
+ const menuElement = tempDiv.querySelector(`#${dropdownId}`);
111
+
112
+ if (menuElement) {
113
+ // Clone the menu and append to body
114
+ menu = menuElement.cloneNode(true);
115
+ menu.setAttribute('id', dropdownId);
116
+ document.body.appendChild(menu);
117
+ el.setAttribute('popovertarget', dropdownId);
118
+
119
+ // Initialize Alpine on the menu
120
+ Alpine.initTree(menu);
121
+
122
+ // Set up the dropdown after menu is ready
123
+ setupDropdown();
124
+ } else {
125
+ console.warn(`[Manifest] Menu with id "${dropdownId}" not found in component "${componentName}"`);
126
+ }
127
+ } else {
128
+ console.warn(`[Manifest] Failed to load component "${componentName}" for dropdown`);
129
+ }
130
+ };
131
+
132
+ // Wait for components to be ready, then try to load
133
+ if (window.__induxComponentsInitialized) {
134
+ waitForComponent();
135
+ } else {
136
+ window.addEventListener('indux:components-ready', waitForComponent);
137
+ }
138
+ return; // Exit early, setup will happen in waitForComponent
139
+ }
140
+ }
141
+
142
+ console.warn(`[Manifest] Dropdown menu with id "${dropdownId}" not found`);
143
+ return;
144
+ }
145
+ el.setAttribute('popovertarget', dropdownId);
146
+ }
147
+
148
+ // Set up the dropdown
149
+ setupDropdown();
150
+
151
+ function setupDropdown() {
152
+ if (!menu) return;
153
+
154
+ // Set up popover
155
+ menu.setAttribute('popover', '');
156
+
157
+ // Set up anchor positioning
158
+ const anchorName = `--dropdown-${anchorCode}`;
159
+ el.style.setProperty('anchor-name', anchorName);
160
+ menu.style.setProperty('position-anchor', anchorName);
161
+
162
+ // Set up hover functionality after menu is ready
163
+ if (modifiers.includes('hover')) {
164
+ const handleShowPopover = () => {
165
+ if (menu && !menu.matches(':popover-open')) {
166
+ clearTimeout(hoverTimeout);
167
+ clearTimeout(autoCloseTimeout);
168
+
169
+ menu.showPopover();
170
+ }
171
+ };
172
+
173
+ // Enhanced auto-close when mouse leaves both trigger and menu
174
+ startAutoCloseTimer = () => {
175
+ clearTimeout(autoCloseTimeout);
176
+ autoCloseTimeout = setTimeout(() => {
177
+ if (menu?.matches(':popover-open')) {
178
+ const isOverButton = el.matches(':hover');
179
+ const isOverMenu = menu.matches(':hover');
180
+
181
+ if (!isOverButton && !isOverMenu) {
182
+ menu.hidePopover();
183
+ }
184
+ }
185
+ }, 300); // Small delay to prevent accidental closes
186
+ };
187
+
188
+ el.addEventListener('mouseenter', handleShowPopover);
189
+ el.addEventListener('mouseleave', startAutoCloseTimer);
190
+ }
191
+
192
+
193
+
194
+ // Add keyboard navigation handling
195
+ menu.addEventListener('keydown', (e) => {
196
+ // Get all navigable elements (traditional focusable + li elements)
197
+ const allElements = menu.querySelectorAll(
198
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), li'
199
+ );
200
+ const currentIndex = Array.from(allElements).indexOf(e.target);
201
+
202
+ if (e.key === 'ArrowDown') {
203
+ e.preventDefault();
204
+ const nextIndex = (currentIndex + 1) % allElements.length;
205
+ const nextElement = allElements[nextIndex];
206
+ if (nextElement.tagName === 'LI') {
207
+ nextElement.setAttribute('tabindex', '0');
208
+ }
209
+ nextElement.focus();
210
+ } else if (e.key === 'ArrowUp') {
211
+ e.preventDefault();
212
+ const prevIndex = (currentIndex - 1 + allElements.length) % allElements.length;
213
+ const prevElement = allElements[prevIndex];
214
+ if (prevElement.tagName === 'LI') {
215
+ prevElement.setAttribute('tabindex', '0');
216
+ }
217
+ prevElement.focus();
218
+ } else if (e.key === 'Tab') {
219
+ // Get only traditional focusable elements for tab navigation
220
+ const focusable = menu.querySelectorAll(
221
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
222
+ );
223
+
224
+ // If we're on the last focusable element and tabbing forward
225
+ if (!e.shiftKey && e.target === focusable[focusable.length - 1]) {
226
+ e.preventDefault();
227
+ menu.hidePopover();
228
+ // Focus the next focusable element after the dropdown trigger
229
+ const allFocusable = document.querySelectorAll(
230
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
231
+ );
232
+ const triggerIndex = Array.from(allFocusable).indexOf(el);
233
+ const nextElement = allFocusable[triggerIndex + 1];
234
+ if (nextElement) nextElement.focus();
235
+ }
236
+
237
+ // If we're on the first element and tabbing backward
238
+ if (e.shiftKey && e.target === focusable[0]) {
239
+ menu.hidePopover();
240
+ }
241
+ } else if (e.key === 'Escape') {
242
+ menu.hidePopover();
243
+ el.focus();
244
+ } else if (e.key === 'Enter' || e.key === ' ') {
245
+ // Allow Enter/Space to activate li elements or follow links
246
+ if (e.target.tagName === 'LI') {
247
+ const link = e.target.querySelector('a');
248
+ if (link) {
249
+ e.preventDefault();
250
+ link.click();
251
+ }
252
+ }
253
+ }
254
+ });
255
+
256
+ // Make li elements focusable when menu opens
257
+ menu.addEventListener('toggle', (e) => {
258
+ if (e.newState === 'open') {
259
+ // Set up li elements for keyboard navigation
260
+ const liElements = menu.querySelectorAll('li');
261
+ liElements.forEach((li, index) => {
262
+ if (!li.hasAttribute('tabindex')) {
263
+ li.setAttribute('tabindex', '-1');
264
+ }
265
+ // Focus first li element if no other focusable elements
266
+ if (index === 0 && !menu.querySelector('button, [href], input, select, textarea, [tabindex="0"]')) {
267
+ li.setAttribute('tabindex', '0');
268
+ li.focus();
269
+ }
270
+ });
271
+ }
272
+ });
273
+
274
+ // Add hover functionality for menu
275
+ if (modifiers.includes('hover')) {
276
+ // Simple approach: any mouse activity in the menu area cancels close timer
277
+ menu.addEventListener('mouseenter', () => {
278
+ clearTimeout(autoCloseTimeout);
279
+ clearTimeout(hoverTimeout);
280
+ });
281
+
282
+ menu.addEventListener('mouseleave', () => {
283
+ // Always start timer when leaving menu bounds
284
+ if (startAutoCloseTimer) {
285
+ startAutoCloseTimer();
286
+ }
287
+ });
288
+
289
+ // Add event listeners to all interactive elements inside menu to cancel timers
290
+ const cancelCloseTimer = () => {
291
+ clearTimeout(autoCloseTimeout);
292
+ };
293
+
294
+ // Set up listeners on existing menu items
295
+ const setupMenuItemListeners = () => {
296
+ const menuItems = menu.querySelectorAll('li, button, a, [role="menuitem"]');
297
+ menuItems.forEach(item => {
298
+ item.addEventListener('mouseenter', cancelCloseTimer);
299
+ });
300
+ };
301
+
302
+ // Setup listeners after a brief delay to ensure menu is rendered
303
+ setTimeout(setupMenuItemListeners, 10);
304
+ }
305
+ } // End of setupDropdown function
306
+ });
307
+ });
308
+ });
309
+ }
310
+
311
+ // Track initialization to prevent duplicates
312
+ let dropdownPluginInitialized = false;
313
+
314
+ function ensureDropdownPluginInitialized() {
315
+ if (dropdownPluginInitialized) {
316
+ return;
317
+ }
318
+ if (!window.Alpine || typeof window.Alpine.directive !== 'function') {
319
+ return;
320
+ }
321
+
322
+ dropdownPluginInitialized = true;
323
+ initializeDropdownPlugin();
324
+
325
+ // If elements with x-dropdown already exist, process them
326
+ if (window.Alpine && typeof window.Alpine.initTree === 'function') {
327
+ const existingDropdownElements = document.querySelectorAll('[x-dropdown]');
328
+ existingDropdownElements.forEach(el => {
329
+ if (!el.__x) {
330
+ window.Alpine.initTree(el);
331
+ }
332
+ });
333
+ }
334
+ }
335
+
336
+ // Expose on window for loader to call if needed
337
+ window.ensureDropdownPluginInitialized = ensureDropdownPluginInitialized;
338
+
339
+ // Handle both DOMContentLoaded and alpine:init
340
+ if (document.readyState === 'loading') {
341
+ document.addEventListener('DOMContentLoaded', ensureDropdownPluginInitialized);
342
+ }
343
+
344
+ document.addEventListener('alpine:init', ensureDropdownPluginInitialized);
345
+
346
+ // If Alpine is already initialized when this script loads, initialize immediately
347
+ if (window.Alpine && typeof window.Alpine.directive === 'function') {
348
+ setTimeout(ensureDropdownPluginInitialized, 0);
349
+ } else if (document.readyState === 'complete') {
350
+ // If document is already loaded but Alpine isn't ready yet, wait for it
351
+ const checkAlpine = setInterval(() => {
352
+ if (window.Alpine && typeof window.Alpine.directive === 'function') {
353
+ clearInterval(checkAlpine);
354
+ ensureDropdownPluginInitialized();
355
+ }
356
+ }, 10);
357
+ setTimeout(() => clearInterval(checkAlpine), 5000);
358
+ }
359
+
360
+ // Handle dialog interactions - close dropdowns when dialogs open
361
+ document.addEventListener('click', (event) => {
362
+ const button = event.target.closest('button[popovertarget]');
363
+ if (!button) return;
364
+
365
+ const targetId = button.getAttribute('popovertarget');
366
+ const target = document.getElementById(targetId);
367
+
368
+ if (target && target.tagName === 'DIALOG' && target.hasAttribute('popover')) {
369
+ // Close dropdowns BEFORE the dialog opens to avoid conflicts
370
+ const openDropdowns = document.querySelectorAll('menu[popover]:popover-open');
371
+
372
+ openDropdowns.forEach(dropdown => {
373
+ if (!target.contains(dropdown)) {
374
+ dropdown.hidePopover();
375
+ }
376
+ });
377
+ }
378
+ });
@@ -0,0 +1,169 @@
1
+ /* Manifest Forms */
2
+
3
+ @layer components {
4
+
5
+ /* Group wrapper */
6
+ :where([role=group]:has(button, input, select)):not(.unstyle) {
7
+ display: flex;
8
+ flex-flow: row nowrap;
9
+ align-items: center;
10
+ gap: 0;
11
+ width: fit-content;
12
+ max-width: 100%;
13
+
14
+ &>* {
15
+ flex-basis: auto;
16
+ flex-shrink: 0;
17
+
18
+ &:focus {
19
+ z-index: 1
20
+ }
21
+ }
22
+
23
+ &>*:first-child {
24
+ border-start-end-radius: 0;
25
+ border-end-end-radius: 0
26
+ }
27
+
28
+ &>*:not(:first-child):not(:last-child) {
29
+ border-radius: 0
30
+ }
31
+
32
+ &>*:last-child {
33
+ border-start-start-radius: 0;
34
+ border-end-start-radius: 0
35
+ }
36
+
37
+ &.even>* {
38
+ flex-shrink: initial;
39
+ width: 100%
40
+ }
41
+
42
+ & input {
43
+ width: fit-content
44
+ }
45
+ }
46
+
47
+ :where(form):not(.unstyle) {
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: calc(var(--spacing) * 4);
51
+ width: 100%
52
+ }
53
+
54
+ /* Fieldset wrapper for checkboxes and radios */
55
+ :where(fieldset):not(.unstyle) {
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: 0.375ch calc(var(--spacing) * 2);
59
+ width: 100%;
60
+
61
+ &:has([type=radio], [type=checkbox]) {
62
+ gap: calc(var(--spacing) * 2);
63
+ }
64
+ }
65
+
66
+ :where(fieldset:has(legend)):not(.unstyle) {
67
+ padding: 1ch 1.5ch 1.5ch 1.5ch;
68
+ border-radius: var(--radius, 0.5rem);
69
+ border-color: var(--color-line, oklch(48.3% 0.006422 17.4 / 0.15));
70
+ border-style: solid;
71
+ border-width: 1px;
72
+
73
+ & :where(legend) {
74
+ padding: 0 1.5ch;
75
+ font-size: 0.875rem;
76
+ color: var(--color-content-subtle, oklch(67.4% 0.0318 251.27));
77
+ }
78
+ }
79
+
80
+ /* Label wrapper for radios and checkboxes */
81
+ :where(label, .label):has([type=radio], [type=checkbox]):not(.unstyle) {
82
+ display: flex;
83
+ flex-flow: row;
84
+ align-items: center;
85
+ gap: 1ch;
86
+ outline: 0 none;
87
+ cursor: pointer;
88
+
89
+ &:focus-within {
90
+ box-shadow: 0 0 0 0
91
+ }
92
+ }
93
+
94
+ /* Label wrapper */
95
+ :where(label:not(:has(.label)), .label):has(button, [role=button], [type=button], [type=submit], select, input:not([role=button], [type=checkbox], [type=radio], [type=file], [type=search]), textarea):not(:has(data)):not(.unstyle) {
96
+ display: flex;
97
+ flex-direction: column;
98
+ gap: 0.2ch;
99
+ width: 100%;
100
+ text-indent: calc(var(--radius) / 2);
101
+ cursor: pointer;
102
+
103
+ /* Prevent text indentation of bare label text nodes from being inherited */
104
+ :where(*) {
105
+ text-indent: 0
106
+ }
107
+
108
+ /* Indent label text in spans */
109
+ :where(span:first-of-type) {
110
+ padding-inline-start: calc(var(--radius) / 2)
111
+ }
112
+
113
+ :where(button, [role=button], [type=button], [type=submit], select, input:not([role=button], [type=checkbox], [type=radio], [type=file], [type=search]), textarea) {
114
+ width: 100%;
115
+ max-width: 100%
116
+ }
117
+
118
+ /* Space out stacked labels for search and file inputs */
119
+ :has([type=search], [type=file]) :where([type=search], [type=file]) {
120
+ margin-top: 0.2ch
121
+ }
122
+ }
123
+
124
+ /* Inline label wrapper */
125
+ label:has(data):not(.unstyle) {
126
+ display: flex;
127
+ flex-direction: row;
128
+ justify-content: space-between;
129
+ align-items: center;
130
+ gap: 1rem;
131
+ width: 100%;
132
+
133
+ &:focus-within {
134
+ box-shadow: 0 0 0 0
135
+ }
136
+
137
+ /* Size buttons and inputs */
138
+ & :where(.label, button, input:not([type=checkbox], [type=radio]), select, textarea) {
139
+ width: calc(var(--spacing-field-height) * 8);
140
+ max-width: 50%;
141
+ }
142
+
143
+ /* Focus state for .label wrapper for search and file inputs */
144
+ & .label:focus-within {
145
+ box-shadow: 0 0 0 2px color-mix(in oklch, var(--color-content-stark) 35%, transparent)
146
+ }
147
+
148
+ /* Align textarea label to top */
149
+ &:has(textarea) {
150
+ align-items: start;
151
+
152
+ :where(data) {
153
+ padding-top: calc(var(--spacing))
154
+ }
155
+ }
156
+ }
157
+
158
+ /* Label wrappers for search and file inputs using .label utility */
159
+ label:has(.label):not(.unstyle) {
160
+ justify-content: space-between;
161
+ background-color: transparent;
162
+ cursor: default;
163
+
164
+ :where([type=search]) {
165
+ width: 100%;
166
+ max-width: 100%
167
+ }
168
+ }
169
+ }