bareframe 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +179 -0
  2. package/dist/bareframe.min.js +119 -0
  3. package/dist/components/accordion.js +66 -0
  4. package/dist/components/autocomplete.css +78 -15
  5. package/dist/components/autocomplete.js +220 -10
  6. package/dist/components/avatar.css +129 -17
  7. package/dist/components/avatar.js +47 -10
  8. package/dist/components/breadcrumb.css +63 -17
  9. package/dist/components/breadcrumb.js +140 -5
  10. package/dist/components/button.css +4 -0
  11. package/dist/components/button.js +95 -15
  12. package/dist/components/chart.css +163 -14
  13. package/dist/components/chart.js +59 -4
  14. package/dist/components/checkbox.css +43 -1
  15. package/dist/components/checkbox.js +98 -5
  16. package/dist/components/dialog.css +95 -0
  17. package/dist/components/dialog.js +172 -4
  18. package/dist/components/divider.css +18 -22
  19. package/dist/components/divider.js +31 -3
  20. package/dist/components/drawer.css +68 -18
  21. package/dist/components/drawer.js +84 -4
  22. package/dist/components/edge.css +54 -0
  23. package/dist/components/edge.js +55 -0
  24. package/dist/components/file-upload.css +72 -3
  25. package/dist/components/file-upload.js +186 -4
  26. package/dist/components/input.css +59 -0
  27. package/dist/components/input.js +369 -4
  28. package/dist/components/list.css +11 -0
  29. package/dist/components/list.js +45 -0
  30. package/dist/components/menu.css +20 -0
  31. package/dist/components/menu.js +144 -0
  32. package/dist/components/modal.css +30 -17
  33. package/dist/components/modal.js +68 -4
  34. package/dist/components/nav.css +39 -0
  35. package/dist/components/progress.css +196 -0
  36. package/dist/components/progress.js +304 -0
  37. package/dist/components/radio.css +35 -1
  38. package/dist/components/radio.js +86 -5
  39. package/dist/components/range.css +91 -0
  40. package/dist/components/range.js +250 -0
  41. package/dist/components/select.css +35 -1
  42. package/dist/components/select.js +255 -4
  43. package/dist/components/skeleton.css +108 -21
  44. package/dist/components/skeleton.js +57 -4
  45. package/dist/components/tab.css +9 -1
  46. package/dist/components/tab.js +66 -1
  47. package/dist/components/tag.css +36 -3
  48. package/dist/components/tag.js +32 -0
  49. package/dist/components/toast.css +113 -0
  50. package/dist/components/toast.js +265 -4
  51. package/dist/components/toggle.css +53 -0
  52. package/dist/components/toggle.js +73 -5
  53. package/dist/components/wizard.css +79 -14
  54. package/dist/components/wizard.js +141 -4
  55. package/dist/index.js +5147 -110
  56. package/dist/manifest.json +5 -42
  57. package/dist/themes/aurora.css +47 -0
  58. package/dist/themes/dark.css +12 -2
  59. package/dist/themes/desert.css +37 -0
  60. package/dist/themes/future.css +47 -0
  61. package/dist/themes/layout.css +191 -0
  62. package/dist/themes/light.css +12 -0
  63. package/dist/themes/matrix.css +37 -0
  64. package/dist/themes/modern.css +64 -0
  65. package/dist/themes/nature.css +47 -0
  66. package/dist/themes/nebula.css +37 -0
  67. package/dist/themes/noir.css +37 -0
  68. package/dist/themes/oceanic.css +37 -0
  69. package/dist/themes/retro.css +47 -0
  70. package/dist/themes/simple.css +47 -0
  71. package/dist/themes/sprint.css +12 -0
  72. package/dist/themes/sunrise.css +37 -0
  73. package/dist/themes/system.css +13 -0
  74. package/package.json +9 -2
  75. package/dist/components/alert.css +0 -30
  76. package/dist/components/alert.js +0 -31
  77. package/dist/components/badge.css +0 -30
  78. package/dist/components/badge.js +0 -31
  79. package/dist/components/banner.css +0 -30
  80. package/dist/components/banner.js +0 -31
  81. package/dist/components/bar-chart.css +0 -30
  82. package/dist/components/bar-chart.js +0 -31
  83. package/dist/components/bottom-sheet.css +0 -30
  84. package/dist/components/bottom-sheet.js +0 -31
  85. package/dist/components/button-group.css +0 -30
  86. package/dist/components/button-group.js +0 -31
  87. package/dist/components/chip.css +0 -30
  88. package/dist/components/chip.js +0 -31
  89. package/dist/components/color-picker.css +0 -30
  90. package/dist/components/color-picker.js +0 -31
  91. package/dist/components/context-menu.css +0 -30
  92. package/dist/components/context-menu.js +0 -31
  93. package/dist/components/donut-chart.css +0 -30
  94. package/dist/components/donut-chart.js +0 -31
  95. package/dist/components/expanded-panel.css +0 -30
  96. package/dist/components/expanded-panel.js +0 -31
  97. package/dist/components/footer.css +0 -30
  98. package/dist/components/footer.js +0 -31
  99. package/dist/components/gantt-chart.css +0 -30
  100. package/dist/components/gantt-chart.js +0 -31
  101. package/dist/components/gauge.css +0 -30
  102. package/dist/components/gauge.js +0 -31
  103. package/dist/components/graph.css +0 -30
  104. package/dist/components/graph.js +0 -31
  105. package/dist/components/header.css +0 -30
  106. package/dist/components/header.js +0 -31
  107. package/dist/components/heatmap.css +0 -30
  108. package/dist/components/heatmap.js +0 -31
  109. package/dist/components/line-chart.css +0 -30
  110. package/dist/components/line-chart.js +0 -31
  111. package/dist/components/list-item.css +0 -30
  112. package/dist/components/list-item.js +0 -31
  113. package/dist/components/menu-item.css +0 -30
  114. package/dist/components/menu-item.js +0 -31
  115. package/dist/components/multi-select.css +0 -30
  116. package/dist/components/multi-select.js +0 -31
  117. package/dist/components/notification.css +0 -30
  118. package/dist/components/notification.js +0 -31
  119. package/dist/components/pie-chart.css +0 -30
  120. package/dist/components/pie-chart.js +0 -31
  121. package/dist/components/popover.css +0 -30
  122. package/dist/components/popover.js +0 -31
  123. package/dist/components/progress-bar.css +0 -30
  124. package/dist/components/progress-bar.js +0 -31
  125. package/dist/components/progress-circle.css +0 -30
  126. package/dist/components/progress-circle.js +0 -31
  127. package/dist/components/radio-group.css +0 -30
  128. package/dist/components/radio-group.js +0 -31
  129. package/dist/components/range-slider.css +0 -30
  130. package/dist/components/range-slider.js +0 -31
  131. package/dist/components/rating.css +0 -30
  132. package/dist/components/rating.js +0 -31
  133. package/dist/components/sheet.css +0 -30
  134. package/dist/components/sheet.js +0 -31
  135. package/dist/components/slider.css +0 -30
  136. package/dist/components/slider.js +0 -31
  137. package/dist/components/snackbar.css +0 -30
  138. package/dist/components/snackbar.js +0 -31
  139. package/dist/components/sparkline.css +0 -30
  140. package/dist/components/sparkline.js +0 -31
  141. package/dist/components/stepper.css +0 -30
  142. package/dist/components/stepper.js +0 -31
  143. package/dist/components/switch.css +0 -30
  144. package/dist/components/switch.js +0 -31
  145. package/dist/components/tab-group.css +0 -30
  146. package/dist/components/tab-group.js +0 -31
  147. package/dist/components/textfield.css +0 -30
  148. package/dist/components/textfield.js +0 -31
  149. package/dist/components/tooltip.css +0 -30
  150. package/dist/components/tooltip.js +0 -31
  151. package/dist/components/treemap.css +0 -30
  152. package/dist/components/treemap.js +0 -31
  153. package/dist/components/upload-dropzone.css +0 -30
  154. package/dist/components/upload-dropzone.js +0 -31
@@ -1,11 +1,15 @@
1
1
  class BfDrawer extends HTMLElement {
2
+ static observedAttributes = ['open', 'side', 'label', 'left', 'right', 'top', 'bottom'];
3
+
2
4
  constructor() {
3
5
  super();
4
6
  this.attachShadow({ mode: 'open' });
7
+ this._onBackdropClick = this._onBackdropClick.bind(this);
5
8
  }
6
9
 
7
10
  connectedCallback() {
8
11
  if (this._initialized) {
12
+ this._sync();
9
13
  return;
10
14
  }
11
15
  this._initialized = true;
@@ -18,13 +22,89 @@ class BfDrawer extends HTMLElement {
18
22
  const root = document.createElement('div');
19
23
  root.className = 'root';
20
24
  root.setAttribute('part', 'root');
21
- root.innerHTML = '<slot></slot>';
25
+ root.innerHTML = `
26
+ <div class="backdrop" part="backdrop"></div>
27
+ <div class="panel" part="panel"><slot></slot></div>
28
+ `;
29
+
30
+ this.shadowRoot.replaceChildren(link, root);
31
+ this._root = root;
32
+ this._backdrop = root.querySelector('.backdrop');
33
+ this._backdrop.addEventListener('click', this._onBackdropClick);
22
34
 
23
- if (!this.innerHTML.trim()) {
24
- root.textContent = 'drawer';
35
+ if (!this.textContent?.trim()) {
36
+ this.textContent = 'drawer';
25
37
  }
26
38
 
27
- this.shadowRoot.replaceChildren(link, root);
39
+ this._sync();
40
+ }
41
+
42
+ attributeChangedCallback() {
43
+ this._sync();
44
+ }
45
+
46
+ open() {
47
+ this.setAttribute('open', '');
48
+ }
49
+
50
+ close() {
51
+ this.removeAttribute('open');
52
+ }
53
+
54
+ toggle() {
55
+ if (this.hasAttribute('open')) {
56
+ this.close();
57
+ return;
58
+ }
59
+ this.open();
60
+ }
61
+
62
+ _onBackdropClick() {
63
+ if (this.hasAttribute('persistent')) {
64
+ return;
65
+ }
66
+ this.close();
67
+ }
68
+
69
+ _side() {
70
+ const explicit = (this.getAttribute('side') || '').toLowerCase();
71
+ if (['left', 'right', 'top', 'bottom'].includes(explicit)) {
72
+ return explicit;
73
+ }
74
+ if (this.hasAttribute('left')) {
75
+ return 'left';
76
+ }
77
+ if (this.hasAttribute('top')) {
78
+ return 'top';
79
+ }
80
+ if (this.hasAttribute('bottom')) {
81
+ return 'bottom';
82
+ }
83
+ return 'right';
84
+ }
85
+
86
+ _shouldOpenByDefault() {
87
+ return !this.hasAttribute('id');
88
+ }
89
+
90
+ _sync() {
91
+ if (!this._root) {
92
+ return;
93
+ }
94
+ if (!this.hasAttribute('open') && this._shouldOpenByDefault()) {
95
+ this.setAttribute('open', '');
96
+ return;
97
+ }
98
+
99
+ const isOpen = this.hasAttribute('open');
100
+ this.hidden = !isOpen;
101
+ this._root.setAttribute('data-open', isOpen ? 'true' : 'false');
102
+ this._root.setAttribute('data-side', this._side());
103
+ this.setAttribute('aria-hidden', String(!isOpen));
104
+ this.setAttribute('role', 'dialog');
105
+ if (this.getAttribute('label')) {
106
+ this.setAttribute('aria-label', this.getAttribute('label'));
107
+ }
28
108
  }
29
109
  }
30
110
 
@@ -0,0 +1,54 @@
1
+ :host {
2
+ --bf-edge-font: var(--bf-theme-font-family, inherit);
3
+ --bf-edge-radius: var(--bf-theme-radius-md, 8px);
4
+ --bf-edge-border-width: var(--bf-theme-border-width, 1px);
5
+ --bf-edge-border-style: var(--bf-theme-border-style, solid);
6
+ --bf-edge-border-color: var(--bf-theme-header-border-color, var(--bf-theme-footer-border-color, var(--bf-theme-border-1, #cbd5e1)));
7
+ --bf-edge-bg: var(--bf-theme-header-bg, var(--bf-theme-footer-bg, var(--bf-theme-surface-1, #ffffff)));
8
+ --bf-edge-color: var(--bf-theme-header-color, var(--bf-theme-footer-color, var(--bf-theme-text-1, #0f172a)));
9
+ --bf-edge-padding-y: var(--bf-theme-space-2, 0.6rem);
10
+ --bf-edge-padding-x: var(--bf-theme-space-3, 0.9rem);
11
+ --bf-edge-transition:
12
+ var(--bf-theme-transition-bg, background-color 120ms ease),
13
+ var(--bf-theme-transition-color, color 120ms ease),
14
+ var(--bf-theme-transition-border, border-color 120ms ease);
15
+
16
+ display: block;
17
+ font: var(--bf-edge-font);
18
+ color: var(--bf-edge-color);
19
+ }
20
+
21
+ .root {
22
+ background: var(--bf-edge-bg);
23
+ color: var(--bf-edge-color);
24
+ border-width: var(--bf-edge-border-width);
25
+ border-style: var(--bf-edge-border-style);
26
+ border-color: var(--bf-edge-border-color);
27
+ border-radius: var(--bf-edge-radius);
28
+ padding: var(--bf-edge-padding-y) var(--bf-edge-padding-x);
29
+ transition: var(--bf-edge-transition);
30
+ }
31
+
32
+ :host([sticky]) {
33
+ position: sticky;
34
+ z-index: var(--bf-theme-z-sticky, 100);
35
+ }
36
+
37
+ :host([fixed]) {
38
+ position: fixed;
39
+ left: 0;
40
+ right: 0;
41
+ z-index: var(--bf-theme-z-fixed, 200);
42
+ }
43
+
44
+ :host([header][sticky]),
45
+ :host([header][fixed]) {
46
+ top: 0;
47
+ bottom: auto;
48
+ }
49
+
50
+ :host([footer][sticky]),
51
+ :host([footer][fixed]) {
52
+ bottom: 0;
53
+ top: auto;
54
+ }
@@ -0,0 +1,55 @@
1
+ class BfEdge extends HTMLElement {
2
+ static observedAttributes = ['position', 'sticky', 'fixed', 'header', 'footer'];
3
+
4
+ constructor() {
5
+ super();
6
+ this.attachShadow({ mode: 'open' });
7
+ }
8
+
9
+ connectedCallback() {
10
+ if (this._initialized) {
11
+ this._syncPosition();
12
+ return;
13
+ }
14
+ this._initialized = true;
15
+
16
+ const cssUrl = new URL('./edge.css', import.meta.url);
17
+ const link = document.createElement('link');
18
+ link.rel = 'stylesheet';
19
+ link.href = cssUrl.href;
20
+
21
+ const root = document.createElement('div');
22
+ root.className = 'root';
23
+ root.setAttribute('part', 'root');
24
+ root.innerHTML = '<slot></slot>';
25
+
26
+ if (!this.innerHTML.trim()) {
27
+ root.textContent = 'edge';
28
+ }
29
+
30
+ this.shadowRoot.replaceChildren(link, root);
31
+ this._syncPosition();
32
+ }
33
+
34
+ attributeChangedCallback() {
35
+ this._syncPosition();
36
+ }
37
+
38
+ _syncPosition() {
39
+ if (!this.hasAttribute('header') && !this.hasAttribute('footer') && !this.getAttribute('position')) {
40
+ this.setAttribute('header', '');
41
+ }
42
+
43
+ const position = this.getAttribute('position');
44
+ if (position === 'bottom') {
45
+ this.removeAttribute('header');
46
+ this.setAttribute('footer', '');
47
+ }
48
+ if (position === 'top') {
49
+ this.removeAttribute('footer');
50
+ this.setAttribute('header', '');
51
+ }
52
+ }
53
+ }
54
+
55
+ customElements.define('bf-edge', BfEdge);
@@ -3,9 +3,18 @@
3
3
  --bf-file-upload-radius: var(--bf-theme-radius-md, 8px);
4
4
  --bf-file-upload-border-width: var(--bf-theme-border-width, 1px);
5
5
  --bf-file-upload-border-style: var(--bf-theme-border-style, solid);
6
- --bf-file-upload-border-color: var(--bf-theme-file-upload-border-color, var(--bf-theme-border-1, #cbd5e1));
7
- --bf-file-upload-bg: var(--bf-theme-file-upload-bg, var(--bf-theme-surface-1, #ffffff));
8
- --bf-file-upload-color: var(--bf-theme-file-upload-color, var(--bf-theme-text-1, #0f172a));
6
+ --bf-file-upload-border-color: var(
7
+ --bf-theme-file-upload-border-color,
8
+ var(--bf-theme-upload-dropzone-border-color, var(--bf-theme-border-1, #cbd5e1))
9
+ );
10
+ --bf-file-upload-bg: var(
11
+ --bf-theme-file-upload-bg,
12
+ var(--bf-theme-upload-dropzone-bg, var(--bf-theme-surface-1, #ffffff))
13
+ );
14
+ --bf-file-upload-color: var(
15
+ --bf-theme-file-upload-color,
16
+ var(--bf-theme-upload-dropzone-color, var(--bf-theme-text-1, #0f172a))
17
+ );
9
18
  --bf-file-upload-padding-y: var(--bf-theme-space-2, 0.6rem);
10
19
  --bf-file-upload-padding-x: var(--bf-theme-space-3, 0.9rem);
11
20
  --bf-file-upload-transition:
@@ -19,6 +28,8 @@
19
28
  }
20
29
 
21
30
  .root {
31
+ display: grid;
32
+ gap: 0.45rem;
22
33
  background: var(--bf-file-upload-bg);
23
34
  color: var(--bf-file-upload-color);
24
35
  border-width: var(--bf-file-upload-border-width);
@@ -27,4 +38,62 @@
27
38
  border-radius: var(--bf-file-upload-radius);
28
39
  padding: var(--bf-file-upload-padding-y) var(--bf-file-upload-padding-x);
29
40
  transition: var(--bf-file-upload-transition);
41
+ cursor: pointer;
42
+ }
43
+
44
+ .root[data-variant='dropzone'] {
45
+ min-height: 7rem;
46
+ align-content: center;
47
+ text-align: center;
48
+ }
49
+
50
+ .root[data-border='dotted'] {
51
+ border-style: dotted;
52
+ }
53
+
54
+ .root[data-border='dashed'] {
55
+ border-style: dashed;
56
+ }
57
+
58
+ .root[data-drag='over'] {
59
+ background: color-mix(in srgb, var(--bf-file-upload-bg) 70%, var(--bf-file-upload-border-color));
60
+ }
61
+
62
+ .root[data-disabled='true'] {
63
+ opacity: 0.65;
64
+ cursor: not-allowed;
65
+ }
66
+
67
+ .native {
68
+ display: none;
69
+ }
70
+
71
+ .label {
72
+ font-weight: 600;
73
+ }
74
+
75
+ .meta {
76
+ color: var(--bf-theme-text-2, #475569);
77
+ font-size: 0.9em;
78
+ }
79
+
80
+ .files {
81
+ display: flex;
82
+ flex-wrap: wrap;
83
+ gap: 0.35rem;
84
+ }
85
+
86
+ .file {
87
+ display: inline-flex;
88
+ align-items: center;
89
+ gap: 0.25rem;
90
+ padding: 0.2rem 0.45rem;
91
+ border-radius: 999px;
92
+ border: 1px solid color-mix(in srgb, var(--bf-file-upload-border-color) 90%, transparent);
93
+ background: color-mix(in srgb, var(--bf-file-upload-bg) 82%, var(--bf-file-upload-border-color));
94
+ font-size: 0.82em;
95
+ }
96
+
97
+ slot:not(:empty) {
98
+ display: block;
30
99
  }
@@ -1,11 +1,27 @@
1
1
  class BfFileUpload extends HTMLElement {
2
+ static observedAttributes = [
3
+ 'variant',
4
+ 'dropzone',
5
+ 'multiple',
6
+ 'accept',
7
+ 'dotted',
8
+ 'dashed',
9
+ 'disabled',
10
+ ];
11
+
2
12
  constructor() {
3
13
  super();
4
14
  this.attachShadow({ mode: 'open' });
15
+ this._onRootClick = this._onRootClick.bind(this);
16
+ this._onInputChange = this._onInputChange.bind(this);
17
+ this._onDragOver = this._onDragOver.bind(this);
18
+ this._onDragLeave = this._onDragLeave.bind(this);
19
+ this._onDrop = this._onDrop.bind(this);
5
20
  }
6
21
 
7
22
  connectedCallback() {
8
23
  if (this._initialized) {
24
+ this._sync();
9
25
  return;
10
26
  }
11
27
  this._initialized = true;
@@ -18,13 +34,179 @@ class BfFileUpload extends HTMLElement {
18
34
  const root = document.createElement('div');
19
35
  root.className = 'root';
20
36
  root.setAttribute('part', 'root');
21
- root.innerHTML = '<slot></slot>';
37
+ root.innerHTML = `
38
+ <input class="native" type="file" />
39
+ <div class="label" part="label"></div>
40
+ <div class="meta" part="meta"></div>
41
+ <div class="files" part="files"></div>
42
+ <slot></slot>
43
+ `;
44
+
45
+ this.shadowRoot.replaceChildren(link, root);
46
+ this._root = root;
47
+ this._input = root.querySelector('.native');
48
+ this._label = root.querySelector('.label');
49
+ this._meta = root.querySelector('.meta');
50
+ this._files = root.querySelector('.files');
51
+
52
+ root.addEventListener('click', this._onRootClick);
53
+ this._input.addEventListener('change', this._onInputChange);
54
+ root.addEventListener('dragover', this._onDragOver);
55
+ root.addEventListener('dragleave', this._onDragLeave);
56
+ root.addEventListener('drop', this._onDrop);
57
+
58
+ this._sync();
59
+ }
60
+
61
+ disconnectedCallback() {
62
+ if (!this._root || !this._input) {
63
+ return;
64
+ }
65
+ this._root.removeEventListener('click', this._onRootClick);
66
+ this._root.removeEventListener('dragover', this._onDragOver);
67
+ this._root.removeEventListener('dragleave', this._onDragLeave);
68
+ this._root.removeEventListener('drop', this._onDrop);
69
+ this._input.removeEventListener('change', this._onInputChange);
70
+ }
22
71
 
23
- if (!this.innerHTML.trim()) {
24
- root.textContent = 'file upload';
72
+ attributeChangedCallback() {
73
+ this._sync();
74
+ }
75
+
76
+ _variant() {
77
+ const explicit = (this.getAttribute('variant') || '').toLowerCase();
78
+ if (explicit === 'dropzone' || this.hasAttribute('dropzone')) {
79
+ return 'dropzone';
25
80
  }
81
+ return 'input';
82
+ }
26
83
 
27
- this.shadowRoot.replaceChildren(link, root);
84
+ _sync() {
85
+ if (!this._root || !this._input || !this._label || !this._meta || !this._files) {
86
+ return;
87
+ }
88
+
89
+ const variant = this._variant();
90
+ this._root.dataset.variant = variant;
91
+ this._root.dataset.border = this.hasAttribute('dotted')
92
+ ? 'dotted'
93
+ : this.hasAttribute('dashed')
94
+ ? 'dashed'
95
+ : 'solid';
96
+
97
+ const disabled = this.hasAttribute('disabled');
98
+ this._root.dataset.disabled = disabled ? 'true' : 'false';
99
+ this._input.disabled = disabled;
100
+
101
+ if (this.hasAttribute('multiple')) {
102
+ this._input.multiple = true;
103
+ this._root.dataset.multiple = 'true';
104
+ } else {
105
+ this._input.multiple = false;
106
+ this._root.dataset.multiple = 'false';
107
+ }
108
+
109
+ const accept = this.getAttribute('accept') || '';
110
+ if (accept) {
111
+ this._input.setAttribute('accept', accept);
112
+ } else {
113
+ this._input.removeAttribute('accept');
114
+ }
115
+
116
+ this._label.textContent =
117
+ this.getAttribute('label') ||
118
+ (variant === 'dropzone'
119
+ ? 'Drop files here or click to browse'
120
+ : 'Choose file');
121
+
122
+ if (!this._selectedFiles || this._selectedFiles.length === 0) {
123
+ this._meta.textContent = this._input.multiple
124
+ ? 'No files selected'
125
+ : 'No file selected';
126
+ this._files.replaceChildren();
127
+ }
128
+ }
129
+
130
+ _onRootClick(event) {
131
+ if (this.hasAttribute('disabled')) {
132
+ return;
133
+ }
134
+ if (event.target instanceof Element && event.target.tagName.toLowerCase() === 'a') {
135
+ return;
136
+ }
137
+ this._input.click();
138
+ }
139
+
140
+ _onInputChange() {
141
+ this._setFiles(this._input.files);
142
+ }
143
+
144
+ _onDragOver(event) {
145
+ if (this._variant() !== 'dropzone' || this.hasAttribute('disabled')) {
146
+ return;
147
+ }
148
+ event.preventDefault();
149
+ this._root.dataset.drag = 'over';
150
+ }
151
+
152
+ _onDragLeave() {
153
+ if (!this._root) {
154
+ return;
155
+ }
156
+ this._root.dataset.drag = 'off';
157
+ }
158
+
159
+ _onDrop(event) {
160
+ if (this._variant() !== 'dropzone' || this.hasAttribute('disabled')) {
161
+ return;
162
+ }
163
+ event.preventDefault();
164
+ this._root.dataset.drag = 'off';
165
+ const list = event.dataTransfer?.files;
166
+ if (!list || list.length === 0) {
167
+ return;
168
+ }
169
+ this._setFiles(list);
170
+ }
171
+
172
+ _setFiles(fileList) {
173
+ const all = Array.from(fileList || []);
174
+ this._selectedFiles = this._input.multiple ? all : all.slice(0, 1);
175
+
176
+ this._meta.textContent = this._selectedFiles.length
177
+ ? `${this._selectedFiles.length} file${this._selectedFiles.length === 1 ? '' : 's'} selected`
178
+ : this._input.multiple
179
+ ? 'No files selected'
180
+ : 'No file selected';
181
+
182
+ const chips = this._selectedFiles.slice(0, 3).map((file) => {
183
+ const item = document.createElement('span');
184
+ item.className = 'file';
185
+ item.textContent = file.name;
186
+ return item;
187
+ });
188
+ if (this._selectedFiles.length > 3) {
189
+ const more = document.createElement('span');
190
+ more.className = 'file';
191
+ more.textContent = `+${this._selectedFiles.length - 3} more`;
192
+ chips.push(more);
193
+ }
194
+ this._files.replaceChildren(...chips);
195
+
196
+ this.dispatchEvent(
197
+ new CustomEvent('bf-change', {
198
+ bubbles: true,
199
+ composed: true,
200
+ detail: {
201
+ count: this._selectedFiles.length,
202
+ files: this._selectedFiles.map((file) => ({
203
+ name: file.name,
204
+ size: file.size,
205
+ type: file.type,
206
+ })),
207
+ },
208
+ }),
209
+ );
28
210
  }
29
211
  }
30
212
 
@@ -19,6 +19,9 @@
19
19
  }
20
20
 
21
21
  .root {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 0.5rem;
22
25
  background: var(--bf-input-bg);
23
26
  color: var(--bf-input-color);
24
27
  border-width: var(--bf-input-border-width);
@@ -28,3 +31,59 @@
28
31
  padding: var(--bf-input-padding-y) var(--bf-input-padding-x);
29
32
  transition: var(--bf-input-transition);
30
33
  }
34
+
35
+ .field {
36
+ width: 100%;
37
+ min-width: 0;
38
+ border: 0;
39
+ outline: 0;
40
+ background: transparent;
41
+ color: inherit;
42
+ font: inherit;
43
+ padding: 0;
44
+ }
45
+
46
+ .swatch,
47
+ .format {
48
+ display: none;
49
+ }
50
+
51
+ .root.is-color .swatch,
52
+ .root.is-color .format {
53
+ display: inline-flex;
54
+ }
55
+
56
+ .swatch {
57
+ width: 1.25rem;
58
+ height: 1.25rem;
59
+ border: 1px solid var(--bf-theme-border-1, #94a3b8);
60
+ border-radius: 4px;
61
+ cursor: pointer;
62
+ flex: 0 0 auto;
63
+ }
64
+
65
+ .format {
66
+ border: 0;
67
+ outline: 0;
68
+ background: transparent;
69
+ color: inherit;
70
+ font: inherit;
71
+ cursor: pointer;
72
+ flex: 0 0 auto;
73
+ }
74
+
75
+ .picker {
76
+ position: absolute;
77
+ opacity: 0;
78
+ pointer-events: none;
79
+ width: 0;
80
+ height: 0;
81
+ }
82
+
83
+ .root.is-color.swatch-right .swatch {
84
+ order: 3;
85
+ }
86
+
87
+ .root.is-color.swatch-hidden .swatch {
88
+ display: none;
89
+ }