Download frontend/src/components/widgets/inputs/DropdownInput.svelte from openfree/graphite2: direct link, hf CLI and curl.
- Browser
- Download file 5.09 kB
-
https://huggingface.co/spaces/openfree/graphite2/resolve/main/frontend/src/components/widgets/inputs/DropdownInput.svelte
- Command line
-
hf download hf://spaces/openfree/graphite2/frontend/src/components/widgets/inputs/DropdownInput.svelte
-
curl -L -o DropdownInput.svelte https://huggingface.co/spaces/openfree/graphite2/resolve/main/frontend/src/components/widgets/inputs/DropdownInput.svelte
5.09 kB
| <script lang="ts"> | |
| import { createEventDispatcher } from "svelte"; | |
| import type { MenuListEntry } from "@graphite/messages"; | |
| import MenuList from "@graphite/components/floating-menus/MenuList.svelte"; | |
| import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; | |
| import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte"; | |
| import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte"; | |
| const DASH_ENTRY = { value: "", label: "-" }; | |
| const dispatch = createEventDispatcher<{ selectedIndex: number; hoverInEntry: number; hoverOutEntry: number }>(); | |
| let menuList: MenuList | undefined; | |
| let self: LayoutRow | undefined; | |
| export let entries: MenuListEntry[][]; | |
| export let selectedIndex: number | undefined = undefined; // When not provided, a dash is displayed | |
| export let drawIcon = false; | |
| export let interactive = true; | |
| export let disabled = false; | |
| export let tooltip: string | undefined = undefined; | |
| export let minWidth = 0; | |
| export let maxWidth = 0; | |
| let activeEntry = makeActiveEntry(); | |
| let activeEntrySkipWatcher = false; | |
| let initialSelectedIndex: number | undefined = undefined; | |
| let open = false; | |
| $: watchSelectedIndex(selectedIndex); | |
| $: watchEntries(entries); | |
| $: watchActiveEntry(activeEntry); | |
| $: watchOpen(open); | |
| function watchOpen(open: boolean) { | |
| initialSelectedIndex = open ? selectedIndex : undefined; | |
| } | |
| // Called only when `selectedIndex` is changed from outside this component | |
| function watchSelectedIndex(_?: typeof selectedIndex) { | |
| activeEntrySkipWatcher = true; | |
| activeEntry = makeActiveEntry(); | |
| } | |
| // Called only when `entries` is changed from outside this component | |
| function watchEntries(_?: typeof entries) { | |
| activeEntrySkipWatcher = true; | |
| activeEntry = makeActiveEntry(); | |
| } | |
| // Called when the `activeEntry` two-way binding on this component's MenuList component is changed, or by the `selectedIndex()` watcher above (but we want to skip that case) | |
| function watchActiveEntry(activeEntry: MenuListEntry) { | |
| if (activeEntrySkipWatcher) { | |
| activeEntrySkipWatcher = false; | |
| } else if (activeEntry !== DASH_ENTRY) { | |
| // We need to set to the initial value first to track a right history step, as if we hover in initial selection. | |
| if (initialSelectedIndex !== undefined) dispatch("hoverInEntry", initialSelectedIndex); | |
| dispatch("selectedIndex", entries.flat().indexOf(activeEntry)); | |
| } | |
| } | |
| function dispatchHoverInEntry(hoveredEntry: MenuListEntry) { | |
| dispatch("hoverInEntry", entries.flat().indexOf(hoveredEntry)); | |
| } | |
| function dispatchHoverOutEntry() { | |
| if (initialSelectedIndex !== undefined) dispatch("hoverOutEntry", initialSelectedIndex); | |
| } | |
| function makeActiveEntry(): MenuListEntry { | |
| const allEntries = entries.flat(); | |
| if (selectedIndex !== undefined && selectedIndex >= 0 && selectedIndex < allEntries.length) { | |
| return allEntries[selectedIndex]; | |
| } | |
| return DASH_ENTRY; | |
| } | |
| function unFocusDropdownBox(e: FocusEvent) { | |
| const blurTarget = (e.target as HTMLDivElement | undefined)?.closest("[data-dropdown-input]") || undefined; | |
| if (blurTarget !== self?.div?.()) open = false; | |
| } | |
| </script> | |
| <LayoutRow | |
| class="dropdown-input" | |
| styles={{ ...(minWidth > 0 ? { "min-width": `${minWidth}px` } : {}), ...(maxWidth > 0 ? { "max-width": `${maxWidth}px` } : {}) }} | |
| bind:this={self} | |
| data-dropdown-input | |
| > | |
| <LayoutRow | |
| class="dropdown-box" | |
| classes={{ disabled, open }} | |
| {tooltip} | |
| on:click={() => !disabled && (open = true)} | |
| on:blur={unFocusDropdownBox} | |
| tabindex={disabled ? -1 : 0} | |
| data-floating-menu-spawner | |
| > | |
| {#if activeEntry.icon} | |
| <IconLabel class="dropdown-icon" icon={activeEntry.icon} /> | |
| {/if} | |
| <TextLabel class="dropdown-label">{activeEntry.label}</TextLabel> | |
| <IconLabel class="dropdown-arrow" icon="DropdownArrow" /> | |
| </LayoutRow> | |
| <MenuList | |
| on:naturalWidth={({ detail }) => (minWidth = detail)} | |
| {activeEntry} | |
| on:activeEntry={({ detail }) => (activeEntry = detail)} | |
| on:hoverInEntry={({ detail }) => dispatchHoverInEntry(detail)} | |
| on:hoverOutEntry={() => dispatchHoverOutEntry()} | |
| {open} | |
| on:open={({ detail }) => (open = detail)} | |
| {entries} | |
| {drawIcon} | |
| {interactive} | |
| direction="Bottom" | |
| scrollableY={true} | |
| bind:this={menuList} | |
| /> | |
| </LayoutRow> | |
| <style lang="scss" global> | |
| .dropdown-input { | |
| position: relative; | |
| .dropdown-box { | |
| align-items: center; | |
| white-space: nowrap; | |
| background: var(--color-1-nearblack); | |
| height: 24px; | |
| border-radius: 2px; | |
| .dropdown-label { | |
| margin: 0; | |
| margin-left: 8px; | |
| flex: 1 1 100%; | |
| } | |
| .dropdown-icon { | |
| margin: 4px 8px; | |
| flex: 0 0 auto; | |
| & + .dropdown-label { | |
| margin-left: 0; | |
| } | |
| } | |
| .dropdown-arrow { | |
| margin: 4px; | |
| margin-right: 2px; | |
| flex: 0 0 auto; | |
| } | |
| &:hover, | |
| &.open { | |
| background: var(--color-4-dimgray); | |
| } | |
| &.disabled { | |
| background: var(--color-2-mildblack); | |
| .text-label { | |
| color: var(--color-8-uppergray); | |
| } | |
| svg { | |
| fill: var(--color-8-uppergray); | |
| } | |
| } | |
| } | |
| .menu-list .floating-menu-container .floating-menu-content { | |
| max-height: 400px; | |
| } | |
| } | |
| </style> | |