fluent-svelte-extra 2.1.3 → 2.1.5

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.
@@ -131,6 +131,11 @@
131
131
  inset-block-start: var(--menu-offset, 0);
132
132
  inset-inline-start: 0;
133
133
 
134
+ &.auto-width {
135
+ inline-size: max-content;
136
+ min-inline-size: calc(100% + 8px);
137
+ }
138
+
134
139
  &.acrylic {
135
140
  background-color: var(--acrylic-fallback-background-base);
136
141
  background-image: var(--acrylic-noise-asset-alpha);
@@ -21,6 +21,18 @@ export let disabled = false;
21
21
  export let open = false;
22
22
  /** Wheter to use acrylic styling for the dropdown menu. */
23
23
  export let acrylic = true;
24
+ /**
25
+ * Specifies the direction the menu should open.
26
+ * 'auto' (default): Centers the selected item.
27
+ * 'top': Forces menu to open upwards (overlaying the button).
28
+ * 'bottom': Forces menu to open downwards (overlaying the button).
29
+ */
30
+ export let direction = "auto";
31
+ /**
32
+ * If true, the dropdown menu will expand horizontally to fit the widest item
33
+ * instead of being constrained to the width of the trigger button.
34
+ */
35
+ export let autoWidth = false;
24
36
  /** Specifies a custom class name for the outer combobox container. */
25
37
  let className = "";
26
38
  export { className as class };
@@ -66,11 +78,15 @@ $: if (items.length > 0) {
66
78
  }
67
79
  $: dispatch("select", selection);
68
80
  $: menuGrowDirection =
69
- !selection || items[items.indexOf(selection)] === items[Math.floor(items.length / 2)]
70
- ? "center"
71
- : items.indexOf(selection) < items.indexOf(items[Math.floor(items.length / 2)])
72
- ? "top"
73
- : "bottom";
81
+ direction !== "auto"
82
+ ? direction === "top"
83
+ ? "bottom"
84
+ : "top"
85
+ : !selection || items[items.indexOf(selection)] === items[Math.floor(items.length / 2)]
86
+ ? "center"
87
+ : items.indexOf(selection) < items.indexOf(items[Math.floor(items.length / 2)])
88
+ ? "top"
89
+ : "bottom";
74
90
  let inputFocused = false;
75
91
  let itemHeight = 36;
76
92
  const maxItems = 14; // 504 (`max-block-size` in ComboBox.scss) / 36 (itemHeight)
@@ -101,8 +117,18 @@ async function openMenu() {
101
117
  await tick();
102
118
  if (editable && searchInputElement)
103
119
  searchInputElement.focus();
104
- if (menuElement && selection)
120
+ if (direction === "auto" && menuElement && selection) {
105
121
  updateOffset(menuElement.children[items.indexOf(selection)]);
122
+ }
123
+ else if (containerElement) {
124
+ if (direction === "top") {
125
+ const visibleItems = items.length > maxItems ? maxItems : items.length;
126
+ menuOffset = containerElement.offsetHeight - visibleItems * itemHeight;
127
+ }
128
+ else if (direction === "bottom") {
129
+ menuOffset = 0;
130
+ }
131
+ }
106
132
  }
107
133
  async function handleKeyboardNavigation(event) {
108
134
  const { key } = event;
@@ -292,6 +318,7 @@ When the combo box is closed, it either displays the current selection or is emp
292
318
  : `${dropdownId}-item-${items.indexOf(selection)}`}
293
319
  role="listbox"
294
320
  class:acrylic
321
+ class:auto-width={autoWidth}
295
322
  class="combo-box-dropdown direction-{!editable
296
323
  ? (menuGrowDirection ?? 'center')
297
324
  : 'top'}"
@@ -325,4 +352,4 @@ When the combo box is closed, it either displays the current selection or is emp
325
352
  {/if}
326
353
  </div>
327
354
 
328
- <style >@-webkit-keyframes menu-in{0%{-webkit-clip-path:var(--fds-grow-clip-path);clip-path:var(--fds-grow-clip-path)}to{-webkit-clip-path:polygon(0 0,100% 0,100% 100%,0 100%);clip-path:polygon(0 0,100% 0,100% 100%,0 100%)}}@keyframes menu-in{0%{-webkit-clip-path:var(--fds-grow-clip-path);clip-path:var(--fds-grow-clip-path)}to{-webkit-clip-path:polygon(0 0,100% 0,100% 100%,0 100%);clip-path:polygon(0 0,100% 0,100% 100%,0 100%)}}@-webkit-keyframes shadow-in{0%{box-shadow:none}to{box-shadow:var(--fds-flyout-shadow)}}@keyframes shadow-in{0%{box-shadow:none}to{box-shadow:var(--fds-flyout-shadow)}}.combo-box{display:inline-flex;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.combo-box :global(.button),.combo-box :global(.text-box){flex:1 1 auto}.combo-box :global(.text-box){border-color:var(--fds-control-border-default)}.combo-box :global(.text-box-underline:after){border-color:transparent}.combo-box :global(.text-box-container){cursor:default}.combo-box :global(.text-box-container:focus-visible){cursor:text}.combo-box.editable :global(.combo-box-textbox:not(:focus-within)){border-color:var(--fds-control-border-default);cursor:default}.combo-box.editable :global(.combo-box-textbox:not(:focus-within)) :global(.text-box-underline:after){content:none}.combo-box.editable :global(.combo-box-textbox.disabled){border-color:var(--fds-control-stroke-default)}.combo-box.editable.open :global(.combo-box-textbox){background-color:var(--fds-control-fill-input-active);cursor:text}.combo-box.editable.open :global(.combo-box-textbox) :global(.text-box-underline:after){border-bottom:2px solid var(--fds-accent-default);content:""}.combo-box.editable.open :global(.combo-box-textbox) :global(input::-moz-placeholder){color:var(--fds-text-tertiary)}.combo-box.editable.open :global(.combo-box-textbox) :global(input:-ms-input-placeholder){color:var(--fds-text-tertiary)}.combo-box.editable.open :global(.combo-box-textbox) :global(input::placeholder){color:var(--fds-text-tertiary)}.combo-box.editable.open :global(.text-box-underline){border-end-end-radius:0;border-end-start-radius:0}.combo-box.editable .combo-box-dropdown{border-radius:var(--fds-overlay-corner-radius);border-start-end-radius:0;border-start-start-radius:0;inline-size:100%;inset-block-start:100%;inset-inline-start:0;margin:0}.combo-box.editable .combo-box-icon{margin:0}.combo-box-label{flex:1 1 auto;min-block-size:20px;text-align:start}.combo-box-label.placeholder{color:var(--fds-text-secondary)}.combo-box.disabled .placeholder{color:var(--fds-text-disabled)}.combo-box-icon{-webkit-margin-start:8px;block-size:12px;inline-size:12px;margin-inline-start:8px}.combo-box-dropdown{-webkit-margin-before:-6px;-webkit-margin-start:-5px;-webkit-animation:menu-in var(--fds-control-normal-duration) var(--fds-control-fast-out-slow-in-easing),shadow-in var(--fds-control-normal-duration) var(--fds-control-fast-out-slow-in-easing) var(--fds-control-normal-duration);animation:menu-in var(--fds-control-normal-duration) var(--fds-control-fast-out-slow-in-easing),shadow-in var(--fds-control-normal-duration) var(--fds-control-fast-out-slow-in-easing) var(--fds-control-normal-duration);background-clip:padding-box;background-color:var(--fds-solid-background-quarternary);border:1px solid var(--fds-surface-stroke-flyout);border-radius:var(--fds-overlay-corner-radius);box-shadow:var(--fds-flyout-shadow);box-sizing:border-box;inline-size:calc(100% + 8px);inset-block-start:var(--fds-menu-offset,0);inset-inline-start:0;margin:0;margin-block-start:-6px;margin-inline-start:-5px;max-block-size:504px;overflow:auto;padding:1px;position:absolute;z-index:100}.combo-box-dropdown.acrylic{-webkit-backdrop-filter:var(--fds-acrylic-fallback-filter);backdrop-filter:var(--fds-acrylic-fallback-filter);background-clip:border-box;background-color:var(--fds-acrylic-fallback-background-base);background-image:var(--fds-acrylic-noise-asset-alpha)}@supports (overflow:overlay){.combo-box-dropdown{overflow:overlay}}.combo-box-dropdown.direction-top{--fds-grow-clip-path:polygon(0 0,100% 0,100% 25%,0 25%)}.combo-box-dropdown.direction-center{--fds-grow-clip-path:polygon(0 25%,100% 24%,100% 75%,0 75%)}.combo-box-dropdown.direction-bottom{--fds-grow-clip-path:polygon(0 75%,100% 75%,100% 100%,0 100%)}</style>
355
+ <style >@-webkit-keyframes menu-in{0%{-webkit-clip-path:var(--fds-grow-clip-path);clip-path:var(--fds-grow-clip-path)}to{-webkit-clip-path:polygon(0 0,100% 0,100% 100%,0 100%);clip-path:polygon(0 0,100% 0,100% 100%,0 100%)}}@keyframes menu-in{0%{-webkit-clip-path:var(--fds-grow-clip-path);clip-path:var(--fds-grow-clip-path)}to{-webkit-clip-path:polygon(0 0,100% 0,100% 100%,0 100%);clip-path:polygon(0 0,100% 0,100% 100%,0 100%)}}@-webkit-keyframes shadow-in{0%{box-shadow:none}to{box-shadow:var(--fds-flyout-shadow)}}@keyframes shadow-in{0%{box-shadow:none}to{box-shadow:var(--fds-flyout-shadow)}}.combo-box{display:inline-flex;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.combo-box :global(.button),.combo-box :global(.text-box){flex:1 1 auto}.combo-box :global(.text-box){border-color:var(--fds-control-border-default)}.combo-box :global(.text-box-underline:after){border-color:transparent}.combo-box :global(.text-box-container){cursor:default}.combo-box :global(.text-box-container:focus-visible){cursor:text}.combo-box.editable :global(.combo-box-textbox:not(:focus-within)){border-color:var(--fds-control-border-default);cursor:default}.combo-box.editable :global(.combo-box-textbox:not(:focus-within)) :global(.text-box-underline:after){content:none}.combo-box.editable :global(.combo-box-textbox.disabled){border-color:var(--fds-control-stroke-default)}.combo-box.editable.open :global(.combo-box-textbox){background-color:var(--fds-control-fill-input-active);cursor:text}.combo-box.editable.open :global(.combo-box-textbox) :global(.text-box-underline:after){border-bottom:2px solid var(--fds-accent-default);content:""}.combo-box.editable.open :global(.combo-box-textbox) :global(input::-moz-placeholder){color:var(--fds-text-tertiary)}.combo-box.editable.open :global(.combo-box-textbox) :global(input:-ms-input-placeholder){color:var(--fds-text-tertiary)}.combo-box.editable.open :global(.combo-box-textbox) :global(input::placeholder){color:var(--fds-text-tertiary)}.combo-box.editable.open :global(.text-box-underline){border-end-end-radius:0;border-end-start-radius:0}.combo-box.editable .combo-box-dropdown{border-radius:var(--fds-overlay-corner-radius);border-start-end-radius:0;border-start-start-radius:0;inline-size:100%;inset-block-start:100%;inset-inline-start:0;margin:0}.combo-box.editable .combo-box-icon{margin:0}.combo-box-label{flex:1 1 auto;min-block-size:20px;text-align:start}.combo-box-label.placeholder{color:var(--fds-text-secondary)}.combo-box.disabled .placeholder{color:var(--fds-text-disabled)}.combo-box-icon{-webkit-margin-start:8px;block-size:12px;inline-size:12px;margin-inline-start:8px}.combo-box-dropdown{-webkit-margin-before:-6px;-webkit-margin-start:-5px;-webkit-animation:menu-in var(--fds-control-normal-duration) var(--fds-control-fast-out-slow-in-easing),shadow-in var(--fds-control-normal-duration) var(--fds-control-fast-out-slow-in-easing) var(--fds-control-normal-duration);animation:menu-in var(--fds-control-normal-duration) var(--fds-control-fast-out-slow-in-easing),shadow-in var(--fds-control-normal-duration) var(--fds-control-fast-out-slow-in-easing) var(--fds-control-normal-duration);background-clip:padding-box;background-color:var(--fds-solid-background-quarternary);border:1px solid var(--fds-surface-stroke-flyout);border-radius:var(--fds-overlay-corner-radius);box-shadow:var(--fds-flyout-shadow);box-sizing:border-box;inline-size:calc(100% + 8px);inset-block-start:var(--fds-menu-offset,0);inset-inline-start:0;margin:0;margin-block-start:-6px;margin-inline-start:-5px;max-block-size:504px;overflow:auto;padding:1px;position:absolute;z-index:100}.combo-box-dropdown.auto-width{inline-size:-webkit-max-content;inline-size:-moz-max-content;inline-size:max-content;min-inline-size:calc(100% + 8px)}.combo-box-dropdown.acrylic{-webkit-backdrop-filter:var(--fds-acrylic-fallback-filter);backdrop-filter:var(--fds-acrylic-fallback-filter);background-clip:border-box;background-color:var(--fds-acrylic-fallback-background-base);background-image:var(--fds-acrylic-noise-asset-alpha)}@supports (overflow:overlay){.combo-box-dropdown{overflow:overlay}}.combo-box-dropdown.direction-top{--fds-grow-clip-path:polygon(0 0,100% 0,100% 25%,0 25%)}.combo-box-dropdown.direction-center{--fds-grow-clip-path:polygon(0 25%,100% 24%,100% 75%,0 75%)}.combo-box-dropdown.direction-bottom{--fds-grow-clip-path:polygon(0 75%,100% 75%,100% 100%,0 100%)}</style>
@@ -14,6 +14,8 @@ declare const __propDef: {
14
14
  disabled?: boolean;
15
15
  open?: boolean;
16
16
  acrylic?: boolean;
17
+ direction?: "auto" | "top" | "bottom";
18
+ autoWidth?: boolean;
17
19
  class?: string;
18
20
  inputElement?: HTMLInputElement;
19
21
  searchInputElement?: HTMLInputElement;
@@ -51,7 +51,9 @@ export let bufferElement = null;
51
51
  let dragging = false;
52
52
  let holding = false;
53
53
  let directionAwareReverse = false;
54
+ // also adding client height will enable users to create distinct shapes for the thumb
54
55
  let thumbClientWidth = 20;
56
+ let thumbClientHeight = 20;
55
57
  $: if (containerElement) {
56
58
  directionAwareReverse =
57
59
  (window === null || window === void 0 ? void 0 : window.getComputedStyle(containerElement).direction) === "ltr" ? reverse : !reverse;
@@ -90,22 +92,35 @@ function calculateValue(event) {
90
92
  const { top, bottom, left, right, width, height } = railElement.getBoundingClientRect();
91
93
  const percentageX = event.touches ? event.touches[0].clientX : event.clientX;
92
94
  const percentageY = event.touches ? event.touches[0].clientY : event.clientY;
93
- const position = orientation === "horizontal" ? percentageX : percentageY;
94
- const startingPos = orientation === "horizontal"
95
- ? directionAwareReverse
96
- ? right
97
- : left
98
- : directionAwareReverse
99
- ? top
100
- : bottom;
101
- const length = orientation === "horizontal" ? width : height;
102
- let nextStep = min +
103
- Math.round(((max - min) *
104
- ((position - startingPos) / length) *
105
- (directionAwareReverse ? -1 : 1) *
106
- (orientation === "vertical" ? -1 : 1)) /
107
- step) *
108
- step;
95
+ const thumbSize = orientation === "horizontal" ? thumbClientWidth : thumbClientHeight;
96
+ const totalLength = orientation === "horizontal" ? width : height;
97
+ const effectiveLength = totalLength - thumbSize;
98
+ if (effectiveLength <= 0)
99
+ return;
100
+ let distanceFromMin = 0;
101
+ if (orientation === "horizontal") {
102
+ if (directionAwareReverse) {
103
+ distanceFromMin = right - percentageX;
104
+ }
105
+ else {
106
+ distanceFromMin = percentageX - left;
107
+ }
108
+ }
109
+ else {
110
+ if (directionAwareReverse) {
111
+ distanceFromMin = percentageY - top;
112
+ }
113
+ else {
114
+ distanceFromMin = bottom - percentageY;
115
+ }
116
+ }
117
+ const relativePos = distanceFromMin - thumbSize / 2;
118
+ let percent = relativePos / effectiveLength;
119
+ if (percent < 0)
120
+ percent = 0;
121
+ if (percent > 1)
122
+ percent = 1;
123
+ let nextStep = min + Math.round(((max - min) * percent) / step) * step;
109
124
  if (nextStep <= min)
110
125
  nextStep = min;
111
126
  else if (nextStep >= max)
@@ -235,6 +250,7 @@ A slider is a control that lets the user select from a range of values by moving
235
250
  aria-valuenow={value}
236
251
  bind:this={thumbElement}
237
252
  bind:clientWidth={thumbClientWidth}
253
+ bind:clientHeight={thumbClientHeight}
238
254
  on:mousedown|preventDefault={() => {
239
255
  sliderThumbHolding = true;
240
256
  dispatch("userHoldStart");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluent-svelte-extra",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "A faithful implementation of Microsoft's Fluent Design System in Svelte.",
5
5
  "homepage": "https://github.com/OpenAnime/fluent-svelte-extra",
6
6
  "license": "MIT",