@ngneers/controls 0.0.1-next.0
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.
- package/fesm2022/ngneers-controls-accordion.mjs +234 -0
- package/fesm2022/ngneers-controls-accordion.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api-ng.mjs +959 -0
- package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api-resize.mjs +712 -0
- package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api.mjs +347 -0
- package/fesm2022/ngneers-controls-api.mjs.map +1 -0
- package/fesm2022/ngneers-controls-avatar.mjs +77 -0
- package/fesm2022/ngneers-controls-avatar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-base.mjs +620 -0
- package/fesm2022/ngneers-controls-base.mjs.map +1 -0
- package/fesm2022/ngneers-controls-breadcrumb.mjs +100 -0
- package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -0
- package/fesm2022/ngneers-controls-button-group.mjs +110 -0
- package/fesm2022/ngneers-controls-button-group.mjs.map +1 -0
- package/fesm2022/ngneers-controls-button.mjs +83 -0
- package/fesm2022/ngneers-controls-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs +74 -0
- package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs.map +1 -0
- package/fesm2022/ngneers-controls-calendar.mjs +801 -0
- package/fesm2022/ngneers-controls-calendar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-checkbox.mjs +63 -0
- package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -0
- package/fesm2022/ngneers-controls-chip.mjs +55 -0
- package/fesm2022/ngneers-controls-chip.mjs.map +1 -0
- package/fesm2022/ngneers-controls-default-icons.mjs +82 -0
- package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
- package/fesm2022/ngneers-controls-defer.mjs +72 -0
- package/fesm2022/ngneers-controls-defer.mjs.map +1 -0
- package/fesm2022/ngneers-controls-dialog.mjs +407 -0
- package/fesm2022/ngneers-controls-dialog.mjs.map +1 -0
- package/fesm2022/ngneers-controls-directives.mjs +498 -0
- package/fesm2022/ngneers-controls-directives.mjs.map +1 -0
- package/fesm2022/ngneers-controls-drawer.mjs +271 -0
- package/fesm2022/ngneers-controls-drawer.mjs.map +1 -0
- package/fesm2022/ngneers-controls-edit-inplace.mjs +127 -0
- package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -0
- package/fesm2022/ngneers-controls-errors.mjs +329 -0
- package/fesm2022/ngneers-controls-errors.mjs.map +1 -0
- package/fesm2022/ngneers-controls-filter.mjs +708 -0
- package/fesm2022/ngneers-controls-filter.mjs.map +1 -0
- package/fesm2022/ngneers-controls-hint.mjs +153 -0
- package/fesm2022/ngneers-controls-hint.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n-translations-de.mjs +170 -0
- package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n-translations-en.mjs +170 -0
- package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n.mjs +57 -0
- package/fesm2022/ngneers-controls-i18n.mjs.map +1 -0
- package/fesm2022/ngneers-controls-icon.mjs +195 -0
- package/fesm2022/ngneers-controls-icon.mjs.map +1 -0
- package/fesm2022/ngneers-controls-inplace.mjs +150 -0
- package/fesm2022/ngneers-controls-inplace.mjs.map +1 -0
- package/fesm2022/ngneers-controls-input-field.mjs +165 -0
- package/fesm2022/ngneers-controls-input-field.mjs.map +1 -0
- package/fesm2022/ngneers-controls-input.mjs +72 -0
- package/fesm2022/ngneers-controls-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-item-view.mjs +473 -0
- package/fesm2022/ngneers-controls-item-view.mjs.map +1 -0
- package/fesm2022/ngneers-controls-list-box.mjs +281 -0
- package/fesm2022/ngneers-controls-list-box.mjs.map +1 -0
- package/fesm2022/ngneers-controls-mask-input.mjs +888 -0
- package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-menu.mjs +371 -0
- package/fesm2022/ngneers-controls-menu.mjs.map +1 -0
- package/fesm2022/ngneers-controls-message.mjs +30 -0
- package/fesm2022/ngneers-controls-message.mjs.map +1 -0
- package/fesm2022/ngneers-controls-number-input.mjs +366 -0
- package/fesm2022/ngneers-controls-number-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-paginator.mjs +129 -0
- package/fesm2022/ngneers-controls-paginator.mjs.map +1 -0
- package/fesm2022/ngneers-controls-popover.mjs +215 -0
- package/fesm2022/ngneers-controls-popover.mjs.map +1 -0
- package/fesm2022/ngneers-controls-progress.mjs +98 -0
- package/fesm2022/ngneers-controls-progress.mjs.map +1 -0
- package/fesm2022/ngneers-controls-radio.mjs +152 -0
- package/fesm2022/ngneers-controls-radio.mjs.map +1 -0
- package/fesm2022/ngneers-controls-roving-focus.mjs +323 -0
- package/fesm2022/ngneers-controls-roving-focus.mjs.map +1 -0
- package/fesm2022/ngneers-controls-scroll-shadow.mjs +65 -0
- package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
- package/fesm2022/ngneers-controls-scroller.mjs +308 -0
- package/fesm2022/ngneers-controls-scroller.mjs.map +1 -0
- package/fesm2022/ngneers-controls-select-button.mjs +58 -0
- package/fesm2022/ngneers-controls-select-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-select.mjs +417 -0
- package/fesm2022/ngneers-controls-select.mjs.map +1 -0
- package/fesm2022/ngneers-controls-slider.mjs +167 -0
- package/fesm2022/ngneers-controls-slider.mjs.map +1 -0
- package/fesm2022/ngneers-controls-snackbar.mjs +414 -0
- package/fesm2022/ngneers-controls-snackbar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-spin-buttons.mjs +126 -0
- package/fesm2022/ngneers-controls-spin-buttons.mjs.map +1 -0
- package/fesm2022/ngneers-controls-spinner.mjs +184 -0
- package/fesm2022/ngneers-controls-spinner.mjs.map +1 -0
- package/fesm2022/ngneers-controls-splitter.mjs +604 -0
- package/fesm2022/ngneers-controls-splitter.mjs.map +1 -0
- package/fesm2022/ngneers-controls-state.mjs +185 -0
- package/fesm2022/ngneers-controls-state.mjs.map +1 -0
- package/fesm2022/ngneers-controls-switch.mjs +39 -0
- package/fesm2022/ngneers-controls-switch.mjs.map +1 -0
- package/fesm2022/ngneers-controls-table.mjs +2634 -0
- package/fesm2022/ngneers-controls-table.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tabs.mjs +336 -0
- package/fesm2022/ngneers-controls-tabs.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tag.mjs +30 -0
- package/fesm2022/ngneers-controls-tag.mjs.map +1 -0
- package/fesm2022/ngneers-controls-toast.mjs +350 -0
- package/fesm2022/ngneers-controls-toast.mjs.map +1 -0
- package/fesm2022/ngneers-controls-toggle-button.mjs +70 -0
- package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tooltip.mjs +506 -0
- package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tree.mjs +599 -0
- package/fesm2022/ngneers-controls-tree.mjs.map +1 -0
- package/fesm2022/ngneers-controls-upload.mjs +377 -0
- package/fesm2022/ngneers-controls-upload.mjs.map +1 -0
- package/fesm2022/ngneers-controls-utils-ng.mjs +411 -0
- package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -0
- package/fesm2022/ngneers-controls-utils.mjs +261 -0
- package/fesm2022/ngneers-controls-utils.mjs.map +1 -0
- package/fesm2022/ngneers-controls.mjs +6 -0
- package/fesm2022/ngneers-controls.mjs.map +1 -0
- package/package.json +302 -0
- package/types/ngneers-controls-accordion.d.ts +139 -0
- package/types/ngneers-controls-api-ng.d.ts +364 -0
- package/types/ngneers-controls-api-resize.d.ts +318 -0
- package/types/ngneers-controls-api.d.ts +145 -0
- package/types/ngneers-controls-avatar.d.ts +54 -0
- package/types/ngneers-controls-base.d.ts +277 -0
- package/types/ngneers-controls-breadcrumb.d.ts +272 -0
- package/types/ngneers-controls-button-group.d.ts +46 -0
- package/types/ngneers-controls-button.d.ts +48 -0
- package/types/ngneers-controls-calendar.d.ts +466 -0
- package/types/ngneers-controls-checkbox.d.ts +40 -0
- package/types/ngneers-controls-chip.d.ts +202 -0
- package/types/ngneers-controls-default-icons.d.ts +5 -0
- package/types/ngneers-controls-defer.d.ts +48 -0
- package/types/ngneers-controls-dialog.d.ts +399 -0
- package/types/ngneers-controls-directives.d.ts +171 -0
- package/types/ngneers-controls-drawer.d.ts +308 -0
- package/types/ngneers-controls-edit-inplace.d.ts +263 -0
- package/types/ngneers-controls-errors.d.ts +155 -0
- package/types/ngneers-controls-filter.d.ts +609 -0
- package/types/ngneers-controls-hint.d.ts +88 -0
- package/types/ngneers-controls-i18n-translations-de.d.ts +164 -0
- package/types/ngneers-controls-i18n-translations-en.d.ts +164 -0
- package/types/ngneers-controls-i18n.d.ts +186 -0
- package/types/ngneers-controls-icon.d.ts +68 -0
- package/types/ngneers-controls-inplace.d.ts +98 -0
- package/types/ngneers-controls-input-field.d.ts +248 -0
- package/types/ngneers-controls-input.d.ts +30 -0
- package/types/ngneers-controls-item-view.d.ts +215 -0
- package/types/ngneers-controls-list-box.d.ts +334 -0
- package/types/ngneers-controls-mask-input.d.ts +434 -0
- package/types/ngneers-controls-menu.d.ts +192 -0
- package/types/ngneers-controls-message.d.ts +21 -0
- package/types/ngneers-controls-number-input.d.ts +146 -0
- package/types/ngneers-controls-paginator.d.ts +255 -0
- package/types/ngneers-controls-popover.d.ts +116 -0
- package/types/ngneers-controls-progress.d.ts +49 -0
- package/types/ngneers-controls-radio.d.ts +91 -0
- package/types/ngneers-controls-roving-focus.d.ts +107 -0
- package/types/ngneers-controls-scroll-shadow.d.ts +25 -0
- package/types/ngneers-controls-scroller.d.ts +151 -0
- package/types/ngneers-controls-select-button.d.ts +51 -0
- package/types/ngneers-controls-select.d.ts +409 -0
- package/types/ngneers-controls-slider.d.ts +57 -0
- package/types/ngneers-controls-snackbar.d.ts +330 -0
- package/types/ngneers-controls-spin-buttons.d.ts +69 -0
- package/types/ngneers-controls-spinner.d.ts +117 -0
- package/types/ngneers-controls-splitter.d.ts +465 -0
- package/types/ngneers-controls-state.d.ts +102 -0
- package/types/ngneers-controls-switch.d.ts +18 -0
- package/types/ngneers-controls-table.d.ts +3127 -0
- package/types/ngneers-controls-tabs.d.ts +118 -0
- package/types/ngneers-controls-tag.d.ts +21 -0
- package/types/ngneers-controls-toast.d.ts +298 -0
- package/types/ngneers-controls-toggle-button.d.ts +48 -0
- package/types/ngneers-controls-tooltip.d.ts +189 -0
- package/types/ngneers-controls-tree.d.ts +409 -0
- package/types/ngneers-controls-upload.d.ts +347 -0
- package/types/ngneers-controls-utils-ng.d.ts +129 -0
- package/types/ngneers-controls-utils.d.ts +130 -0
- package/types/ngneers-controls.d.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngneers-controls-tree.mjs","sources":["../../src/tree/tree-templates.ts","../../src/tree/tree-utils.ts","../../src/tree/tree.ts","../../src/tree/tree.html","../../src/tree/ngneers-controls-tree.ts"],"sourcesContent":["import { computed, contentChild, Directive, input, TemplateRef, viewChild } from '@angular/core';\n\nimport { templateTypesFn } from '@ngneers/controls/api/ng';\nimport { ValueControlBase } from '@ngneers/controls/base';\n\nimport type { NgnTreeItem, NgnTreeItemsValue } from '@ngneers/controls/api';\nimport type { InputGeneric } from '@ngneers/controls/utils';\n\n/**\n * Value type for the tree control.\n * * When `multiple` is `true`, the value is an array of node values.\n * * Otherwise it is a single node value.\n *\n * Uses {@link NgnTreeItemsValue} (branch + leaf values) rather than the\n * leaf-only `NgnItemsValue`, since a branch node can be selected too.\n */\nexport type ValueType<Items extends readonly NgnTreeItem[], Multiple extends boolean> =\n InputGeneric<Multiple, false> extends true\n ? NgnTreeItemsValue<Items>[]\n : NgnTreeItemsValue<Items>;\n\n@Directive()\nexport abstract class TreeTemplates<\n Items extends readonly NgnTreeItem[],\n Multiple extends boolean,\n> extends ValueControlBase<'tree', ValueType<Items, Multiple> | null> {\n // Item template\n private readonly _defaultItemTemplate =\n viewChild.required<TemplateRef<typeof this.templateTypes.item>>('defaultItemTemplate');\n private readonly _userItemTemplate =\n contentChild<TemplateRef<typeof this.templateTypes.item>>('item');\n /**\n * Set a custom template for a tree node.\n * Can also be set via an `<ng-template #item>` element. A per-node\n * `item.template` takes precedence over this global template.\n */\n public readonly templateItem = input<TemplateRef<typeof this.templateTypes.item> | null>(null);\n protected readonly itemTemplate = computed(\n () => this._userItemTemplate() ?? this.templateItem() ?? this._defaultItemTemplate()\n );\n\n // Empty template\n private readonly _defaultEmptyTemplate =\n viewChild.required<TemplateRef<unknown>>('defaultEmptyTemplate');\n private readonly _userEmptyTemplate = contentChild<TemplateRef<unknown>>('empty');\n /**\n * Set a custom template for the empty state.\n * Can also be set via an `<ng-template #empty>` element.\n */\n public readonly templateEmpty = input<TemplateRef<unknown> | null>(null);\n protected readonly emptyTemplate = computed(\n () => this._userEmptyTemplate() ?? this.templateEmpty() ?? this._defaultEmptyTemplate()\n );\n\n public readonly templateTypes = templateTypesFn<{\n item: {\n $implicit: Items[number] | undefined;\n level: number;\n expanded: boolean;\n hasChildren: boolean;\n };\n }>();\n}\n","import type { NgnTreeItem } from '@ngneers/controls/api';\n\nexport interface FlatTreeNode<T = any, V = any> {\n item: NgnTreeItem<T, V>;\n /** 0-based depth. */\n level: number;\n /** Number of siblings at this level (aria-setsize). */\n setSize: number;\n /** 1-based position among siblings (aria-posinset). */\n posInSet: number;\n hasChildren: boolean;\n expanded: boolean;\n /** True when an ancestor is disabled (disable cascades to the subtree). */\n parentDisabled: boolean;\n}\n\n/** Walk the tree, emitting only expanded + visible nodes as a flat list. */\nexport function flattenTree<T, V>(\n items: readonly NgnTreeItem<T, V>[],\n expanded: ReadonlySet<V>,\n level = 0,\n parentDisabled = false\n): FlatTreeNode<T, V>[] {\n const result: FlatTreeNode<T, V>[] = [];\n const setSize = items.length;\n items.forEach((item, index) => {\n // A lazy branch shows a toggle before its children are loaded.\n const hasChildren = (!!item.items && item.items.length > 0) || !!item.lazy;\n const isExpanded = hasChildren && expanded.has(item.value);\n const effectiveDisabled = parentDisabled || !!item.disabled;\n result.push({\n item,\n level,\n setSize,\n posInSet: index + 1,\n hasChildren,\n expanded: isExpanded,\n parentDisabled,\n });\n if (isExpanded && item.items) {\n result.push(...flattenTree(item.items, expanded, level + 1, effectiveDisabled));\n }\n });\n return result;\n}\n\n/**\n * Inject lazily-loaded children (keyed by node value) into the tree, returning\n * a new forest where resolved lazy nodes carry their loaded `items` and are no\n * longer marked `lazy`. Returns the input unchanged when nothing is loaded.\n */\nexport function applyLoadedChildren<T, V>(\n items: readonly NgnTreeItem<T, V>[],\n loaded: ReadonlyMap<V, readonly NgnTreeItem<T, V>[]>\n): readonly NgnTreeItem<T, V>[] {\n if (loaded.size === 0) {\n return items;\n }\n return items.map(item => {\n const cached = loaded.get(item.value);\n if (cached !== undefined) {\n return { ...item, items: [...applyLoadedChildren(cached, loaded)], lazy: false };\n }\n if (item.items && item.items.length > 0) {\n return { ...item, items: [...applyLoadedChildren(item.items, loaded)] };\n }\n return item;\n });\n}\n\n/** All selectable, enabled leaf values under (and including) an item. */\nexport function collectLeafValues<T, V>(item: NgnTreeItem<T, V>, parentDisabled = false): V[] {\n const disabled = parentDisabled || !!item.disabled;\n if (item.items && item.items.length > 0) {\n return item.items.flatMap(child => collectLeafValues(child, disabled));\n }\n if (disabled || item.selectable === false) {\n return [];\n }\n return [item.value];\n}\n\n/** All branch values in the forest (used to auto-expand while filtering). */\nexport function collectBranchValues<T, V>(items: readonly NgnTreeItem<T, V>[]): V[] {\n return items.flatMap(item =>\n item.items && item.items.length > 0 ? [item.value, ...collectBranchValues(item.items)] : []\n );\n}\n\n/** Add/remove all of a node's enabled leaf values against the current value set. */\nexport function cascadeCheck<T, V>(\n node: NgnTreeItem<T, V>,\n shouldCheck: boolean,\n currentValue: readonly V[]\n): V[] {\n const leaves = collectLeafValues(node);\n const set = new Set(currentValue);\n for (const value of leaves) {\n if (shouldCheck) {\n set.add(value);\n } else {\n set.delete(value);\n }\n }\n return [...set];\n}\n\n/** Derive a node's tri-state from which of its leaves are in the value set. */\nexport function computeNodeState<T, V>(\n item: NgnTreeItem<T, V>,\n valueSet: ReadonlySet<V>\n): 'checked' | 'unchecked' | 'indeterminate' {\n const leaves = collectLeafValues(item);\n if (leaves.length === 0) {\n return 'unchecked';\n }\n let checked = 0;\n for (const value of leaves) {\n if (valueSet.has(value)) {\n checked++;\n }\n }\n if (checked === 0) return 'unchecked';\n if (checked === leaves.length) return 'checked';\n return 'indeterminate';\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n Component,\n computed,\n effect,\n inject,\n input,\n model,\n type OnInit,\n output,\n runInInjectionContext,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { treeControlTemplate } from '@ngneers/controls-themes/templates/tree';\n\nimport {\n type FilterConfig,\n type FilterConfigInternal,\n filterOptions,\n type NgnTreeItem,\n type NgnTreeItemsValue,\n} from '@ngneers/controls/api';\nimport { NgnStorage, type NgnStorageKind, NgnTemplate } from '@ngneers/controls/api/ng';\nimport { NgnPt, provideSelf } from '@ngneers/controls/base';\nimport { NgnCheckbox } from '@ngneers/controls/checkbox';\nimport { I18n } from '@ngneers/controls/i18n';\nimport { NgnScroller, NgnScrollerItem } from '@ngneers/controls/scroller';\nimport { createConditionalSpinner, NgnSpinner } from '@ngneers/controls/spinner';\nimport { Logger, maybeCallback } from '@ngneers/controls/utils';\nimport { asyncComputed } from '@ngneers/controls/utils-ng';\n\nimport { TreeTemplates, type ValueType } from './tree-templates';\nimport {\n applyLoadedChildren,\n cascadeCheck,\n collectBranchValues,\n computeNodeState,\n type FlatTreeNode,\n flattenTree,\n} from './tree-utils';\n\n/** The individual pieces of tree state that can be persisted. */\nexport type NgnTreeStorageState = 'value' | 'expanded';\n\n/**\n * Configuration for persisting tree state (expansion + selection).\n * Pass to the `storage` input, e.g. `{ key: 'my-tree' }` or\n * `{ key: 'my-tree', kind: 'sessionstorage' }`.\n */\nexport interface NgnTreeStorageConfig {\n /** The storage key under which the state is saved. */\n key: string;\n /**\n * Where/how to persist. Accepts `'localstorage'`, `'sessionstorage'`, or a\n * custom `{ initialize, update }` adapter. Defaults to `'localstorage'`.\n */\n kind?: NgnStorageKind<Record<string, unknown>>;\n /**\n * Which states to persist. When omitted, all states are saved. When\n * provided, only the listed states are saved/restored.\n */\n states?: NgnTreeStorageState[];\n}\n\n/**\n * @category control\n */\n@Component({\n selector: 'ngn-tree',\n templateUrl: './tree.html',\n imports: [\n NgTemplateOutlet,\n NgnScroller,\n NgnScrollerItem,\n NgnCheckbox,\n NgnSpinner,\n NgnTemplate,\n NgnPt,\n ],\n providers: [provideSelf(NgnTree)],\n host: {\n '[attr.tabIndex]': 'focussable() ? 0 : null',\n '(keydown)': 'onKeyDown($event)',\n '(focusout)': 'currentHighlightedValue.set(null)',\n role: 'tree',\n '[attr.aria-activedescendant]':\n 'currentHighlightedValue() != null ? inputId() + \"_node_\" + currentHighlightedValue() : null',\n '[attr.aria-multiselectable]': '!!multiple()',\n '[attr.aria-label]': 'label()',\n '[attr.aria-labelledby]': 'labelledBy()',\n '[id]': 'inputId()',\n },\n})\nexport class NgnTree<Items extends readonly NgnTreeItem[], Multiple extends boolean = false>\n extends TreeTemplates<Items, Multiple>\n implements OnInit\n{\n protected readonly i18n = inject(I18n).translations;\n protected readonly theme = this.injectThemeTemplate(treeControlTemplate, {\n root: true,\n invalid: () => this.invalid(),\n empty: () => !this.flatNodes().length,\n });\n\n private readonly _scroller = viewChild.required<NgnScroller<FlatTreeNode>>(NgnScroller);\n\n /** The tree items to render, as a nested list of {@link NgnTreeItem} objects. */\n public readonly items = input.required<Items>();\n\n /**\n * Whether nodes can be selected/checked.\n * @default true\n */\n public readonly selectable = input(true, { transform: booleanAttribute });\n /**\n * Whether the tree is focusable and participates in keyboard navigation.\n * @default true\n */\n public readonly focussable = input(true, { transform: booleanAttribute });\n /**\n * Whether the tree is virtualized. When enabled, provide {@link itemHeight}.\n * @default false\n */\n public readonly virtual = input(false, { transform: booleanAttribute });\n /**\n * When {@link virtual} is enabled, this defines the height of each node in the list.\n */\n public readonly itemHeight = input<number>();\n /**\n * Enable this to allow selecting multiple nodes. The value then becomes an array of node values.\n * @default false\n */\n public readonly multiple = input<Multiple>();\n /**\n * Whether to render checkboxes. Defaults to `multiple()`.\n * @default multiple()\n */\n public readonly checkbox = input<boolean>();\n /**\n * Boolean to enable filtering, or a `FilterConfig` to customize it.\n * @default false\n */\n public readonly filter = input<FilterConfig<Items[number]> | boolean>(false);\n /** Manually set the filter text. */\n public readonly filterText = input<string | null>(null);\n /** Values of expanded branch nodes (two-way). */\n public readonly expandedValues = model<NgnTreeItemsValue<Items>[]>([]);\n\n /**\n * Persist expansion + selection state across reloads. Provide a key and,\n * optionally, an `NgnStorageKind` (defaults to `'localstorage'`).\n */\n public readonly storage = input<NgnTreeStorageConfig | null>(null);\n private _storage?: NgnStorage<Record<string, unknown>>;\n\n /**\n * Loads the children of a `lazy` node the first time it is expanded.\n * The returned items are cached internally and merged into the tree.\n */\n public readonly loadChildren =\n input<(item: NgnTreeItem) => readonly NgnTreeItem[] | Promise<readonly NgnTreeItem[]>>();\n\n /** Emitted with the node's value whenever a node is clicked/selected. */\n public readonly itemClicked = output<NgnTreeItemsValue<Items>>();\n /** Emitted when a branch is expanded (fires before any lazy load resolves). */\n public readonly nodeExpand = output<NgnTreeItem>();\n\n protected readonly maybeCallback = maybeCallback;\n\n /** Lazily-loaded children, keyed by node value. */\n private readonly _loadedChildren = signal<\n ReadonlyMap<NgnTreeItemsValue<Items>, readonly NgnTreeItem[]>\n >(new Map());\n /** Node values currently awaiting their lazy children. */\n private readonly _loadingNodes = signal<ReadonlySet<NgnTreeItemsValue<Items>>>(new Set());\n\n protected readonly filteredItems = asyncComputed(async () => {\n const filter = !!this.filter();\n const appliedFilterOptions = this._appliedFilterOptions();\n const filterText = this.filterText();\n if (!filter || !filterText) {\n return this.items() as readonly NgnTreeItem[];\n }\n return await filterOptions(this.items(), filterText, appliedFilterOptions);\n }, []);\n\n private readonly _appliedFilterOptions = computed(() => {\n const filter = this.filter();\n const providedFilterArgs = typeof filter === 'boolean' ? {} : filter;\n const options: FilterConfigInternal<NgnTreeItem> = {\n filterFieldsCallback: item => item.label,\n fieldItems: 'items',\n splitWords: true,\n caseSensitive: false,\n filterFn: 'contains',\n ...providedFilterArgs,\n };\n return options;\n });\n\n protected readonly filterIsExecuting = this.filteredItems.isRunning;\n\n /** While filtering, auto-expand every branch that survived the filter. */\n private readonly _autoExpanded = computed(() =>\n this.filterText() ? collectBranchValues(this.filteredItems()) : []\n );\n\n private readonly _expandedSet = computed(\n () => new Set<NgnTreeItemsValue<Items>>([...this.expandedValues(), ...this._autoExpanded()])\n );\n\n /** Filtered items with any lazily-loaded children merged in. */\n private readonly _effectiveItems = computed(() =>\n applyLoadedChildren(this.filteredItems(), this._loadedChildren())\n );\n\n public readonly flatNodes = computed(() =>\n flattenTree(this._effectiveItems(), this._expandedSet())\n );\n\n protected readonly valueArray = computed(() => {\n const v = this.value();\n return (Array.isArray(v) ? v : v != null ? [v] : []) as NgnTreeItemsValue<Items>[];\n });\n private readonly _valueSet = computed(() => new Set(this.valueArray()));\n\n public readonly currentHighlightedValue = signal<NgnTreeItemsValue<Items> | null>(null);\n\n protected readonly showCheckboxes = computed(() => this.checkbox() ?? this.multiple() ?? false);\n\n constructor() {\n super();\n createConditionalSpinner(this.filterIsExecuting);\n\n // Fallback init for when the `storage` config arrives after ngOnInit (e.g.\n // an async binding). Idempotent with the ngOnInit path. Registered before\n // the persistence effect so the store is restored before any write; the\n // persistence effect no-ops until `_storage` exists anyway.\n effect(() => {\n const cfg = this.storage();\n if (cfg) {\n untracked(() => this._initStorage(cfg));\n }\n });\n\n // Persist expansion + selection whenever they change.\n effect(() => {\n const expanded = this.expandedValues();\n const value = this.value();\n untracked(() => {\n if (!this._storage) {\n return;\n }\n if (this._persistsState('expanded')) {\n this._storage.set('expanded', expanded);\n }\n if (this._persistsState('value')) {\n this._storage.set('value', value ?? null);\n }\n });\n });\n\n // Drop cached lazy children when the source items change identity.\n effect(() => {\n this.items();\n untracked(() => {\n if (this._loadedChildren().size || this._loadingNodes().size) {\n this._loadedChildren.set(new Map());\n this._loadingNodes.set(new Set());\n }\n });\n });\n\n effect(() => {\n const highlighted = this.currentHighlightedValue();\n if (highlighted == null) {\n return;\n }\n const index = this.flatNodes().findIndex(node => node.item.value === highlighted);\n if (index >= 0) {\n this._scroller().scrollToIndex(index);\n }\n });\n }\n\n /**\n * Create the store and restore persisted state before the first view check\n * (so it also feeds the SSR render for the `cookie` kind without triggering\n * ExpressionChangedAfterItHasBeenChecked).\n *\n * This is the common path: the `storage` config is normally bound before\n * init, so the restore happens pre-render. When the config only becomes\n * available after init (e.g. an async binding, or a dynamic host that applies\n * inputs after creating the component), the constructor effect below picks it\n * up instead — `_initStorage` is idempotent, so at most one of them runs.\n */\n public ngOnInit(): void {\n const cfg = this.storage();\n if (cfg) {\n this._initStorage(cfg);\n }\n }\n\n /** Create the store and restore persisted state. Runs at most once. */\n private _initStorage(cfg: NgnTreeStorageConfig): void {\n if (this._storage) {\n return;\n }\n // NgnStorage reads platform/document/request tokens, so it needs an\n // injection context.\n this._storage = runInInjectionContext(\n this.injector,\n () =>\n new NgnStorage<Record<string, unknown>>(cfg.key, cfg.kind ?? 'localstorage', {\n expanded: this.expandedValues(),\n value: this.value() ?? null,\n })\n );\n if (this._persistsState('expanded')) {\n const savedExpanded = this._storage.get('expanded');\n if (Array.isArray(savedExpanded)) {\n this.expandedValues.set(savedExpanded as NgnTreeItemsValue<Items>[]);\n }\n }\n if (this._persistsState('value')) {\n const savedValue = this._storage.get('value');\n if (savedValue !== undefined) {\n this.value.set(savedValue as ValueType<Items, Multiple>);\n }\n }\n }\n\n /** Whether the given state should be persisted (all states when unset). */\n private _persistsState(state: NgnTreeStorageState): boolean {\n const states = this.storage()?.states;\n return !states || states.includes(state);\n }\n\n /** Derived tri-state for a node's checkbox / aria-checked. */\n protected nodeState(item: NgnTreeItem): 'checked' | 'unchecked' | 'indeterminate' {\n return computeNodeState(item, this._valueSet());\n }\n\n /** Whether a node can be selected/checked right now. */\n protected isSelectable(node: FlatTreeNode): boolean {\n const disabled = node.parentDisabled || !!node.item.disabled;\n return this.selectable() && node.item.selectable !== false && !disabled;\n }\n\n /** Whether a lazy node is currently loading its children. */\n protected isLoading(item: NgnTreeItem): boolean {\n return this._loadingNodes().has(item.value as NgnTreeItemsValue<Items>);\n }\n\n protected async toggleExpand(item: NgnTreeItem): Promise<void> {\n const value = item.value as NgnTreeItemsValue<Items>;\n const willExpand = !this.expandedValues().includes(value);\n this.expandedValues.update(values =>\n values.includes(value) ? values.filter(v => v !== value) : [...values, value]\n );\n if (!willExpand) {\n return;\n }\n this.nodeExpand.emit(item);\n\n const loader = this.loadChildren();\n if (\n !item.lazy ||\n !loader ||\n this._loadedChildren().has(value) ||\n this._loadingNodes().has(value)\n ) {\n return;\n }\n this._loadingNodes.update(set => new Set(set).add(value));\n let children: readonly NgnTreeItem[] | undefined;\n try {\n children = await loader(item);\n } catch (error) {\n Logger.warn(`ngn-tree: failed to load children for node \"${String(value)}\"`, error);\n }\n // Ignore stale results: if items() changed mid-load, the loading state was\n // cleared and this result should be dropped.\n if (children && this._loadingNodes().has(value)) {\n this._loadedChildren.update(map => new Map(map).set(value, children));\n }\n this._loadingNodes.update(set => {\n const next = new Set(set);\n next.delete(value);\n return next;\n });\n }\n\n protected onRowClick(node: FlatTreeNode): void {\n if (!this.isSelectable(node)) {\n return;\n }\n this.onSelect(node.item);\n }\n\n protected onSelect(item: NgnTreeItem): void {\n const value = item.value as NgnTreeItemsValue<Items>;\n if (this.multiple()) {\n if (this.showCheckboxes()) {\n // Cascade tri-state checking; value stays a leaf-only array.\n const shouldCheck = this.nodeState(item) !== 'checked';\n this.value.set(\n cascadeCheck(item, shouldCheck, this.valueArray()) as ValueType<Items, Multiple>\n );\n } else {\n const current = (this.value() as NgnTreeItemsValue<Items>[]) ?? [];\n this.value.set(\n (current.includes(value)\n ? current.filter(v => v !== value)\n : [...current, value]) as ValueType<Items, Multiple>\n );\n }\n } else {\n // Single-select always stores a single value (never an array), even when\n // checkboxes are shown.\n this.value.set(value as ValueType<Items, Multiple>);\n }\n this.itemClicked.emit(value);\n this.touched.set(true);\n }\n\n public scrollToIndex(index: number): void {\n this._scroller().scrollToIndex(index);\n }\n\n public onKeyDown(event: KeyboardEvent): void {\n const nodes = this.flatNodes();\n if (!nodes.length) {\n return;\n }\n const enabled = nodes.filter(n => !(n.parentDisabled || n.item.disabled));\n const key = event.key;\n\n if (key === 'ArrowDown' || key === 'ArrowUp') {\n event.preventDefault();\n event.stopPropagation();\n this._moveHighlight(enabled, key === 'ArrowDown' ? 1 : -1);\n } else if (key === 'Home' || key === 'End') {\n event.preventDefault();\n event.stopPropagation();\n const target = key === 'Home' ? enabled[0] : enabled[enabled.length - 1];\n this.currentHighlightedValue.set(\n (target?.item.value ?? null) as NgnTreeItemsValue<Items> | null\n );\n } else if (key === 'ArrowRight') {\n event.preventDefault();\n event.stopPropagation();\n this._expandOrEnter(nodes);\n } else if (key === 'ArrowLeft') {\n event.preventDefault();\n event.stopPropagation();\n this._collapseOrLeave(nodes);\n } else if (key === 'Enter' || key === ' ') {\n const node = this._currentNode(nodes);\n if (node && this.isSelectable(node)) {\n event.preventDefault();\n event.stopImmediatePropagation();\n this.onSelect(node.item);\n }\n } else if (key.length === 1 && /\\S/.test(key)) {\n this._typeahead(enabled, key);\n }\n }\n\n private _currentNode(nodes: readonly FlatTreeNode[]): FlatTreeNode | undefined {\n const current = this.currentHighlightedValue();\n return nodes.find(n => n.item.value === current);\n }\n\n private _moveHighlight(enabled: readonly FlatTreeNode[], dir: 1 | -1): void {\n if (!enabled.length) {\n this.currentHighlightedValue.set(null);\n return;\n }\n const current = this.currentHighlightedValue();\n const idx = enabled.findIndex(n => n.item.value === current);\n let next: number;\n if (idx === -1) {\n next = dir === 1 ? 0 : enabled.length - 1;\n } else {\n next = (idx + dir + enabled.length) % enabled.length;\n }\n const target = enabled[next];\n if (target) {\n this.currentHighlightedValue.set(target.item.value as NgnTreeItemsValue<Items>);\n }\n }\n\n private _expandOrEnter(nodes: readonly FlatTreeNode[]): void {\n const node = this._currentNode(nodes);\n if (!node) {\n return;\n }\n if (node.hasChildren && !node.expanded) {\n void this.toggleExpand(node.item);\n } else if (node.hasChildren && node.expanded) {\n const idx = nodes.indexOf(node);\n for (let i = idx + 1; i < nodes.length; i++) {\n const candidate = nodes[i];\n if (!candidate || candidate.level <= node.level) {\n break; // left this node's subtree\n }\n if (!(candidate.parentDisabled || candidate.item.disabled)) {\n this.currentHighlightedValue.set(candidate.item.value as NgnTreeItemsValue<Items>);\n return;\n }\n }\n }\n }\n\n private _collapseOrLeave(nodes: readonly FlatTreeNode[]): void {\n const node = this._currentNode(nodes);\n if (!node) {\n return;\n }\n if (node.hasChildren && node.expanded) {\n void this.toggleExpand(node.item);\n return;\n }\n // move to parent: previous node with a smaller level\n const idx = nodes.indexOf(node);\n for (let i = idx - 1; i >= 0; i--) {\n const ancestor = nodes[i];\n if (!ancestor) {\n continue;\n }\n if (ancestor.level < node.level) {\n if (!(ancestor.parentDisabled || ancestor.item.disabled)) {\n this.currentHighlightedValue.set(ancestor.item.value as NgnTreeItemsValue<Items>);\n }\n return;\n }\n }\n }\n\n private _typeahead(enabled: readonly FlatTreeNode[], char: string): void {\n const lower = char.toLowerCase();\n const current = this.currentHighlightedValue();\n const start = enabled.findIndex(n => n.item.value === current);\n for (let offset = 1; offset <= enabled.length; offset++) {\n const node = enabled[(start + offset + enabled.length) % enabled.length];\n if (!node) {\n continue;\n }\n const label = maybeCallback(node.item.label).toLowerCase();\n if (label.startsWith(lower)) {\n this.currentHighlightedValue.set(node.item.value as NgnTreeItemsValue<Items>);\n return;\n }\n }\n }\n}\n","<ngn-scroller\n [ptInt]=\"this\"\n [ptDep]=\"'scroller'\"\n [virtual]=\"virtual()\"\n [itemHeight]=\"itemHeight()\"\n [items]=\"flatNodes()\"\n [templateItem]=\"usedItemTemplate\"\n #scroller\n/>\n\n@if (!flatNodes().length) {\n <div role=\"status\">\n <ng-container\n *ngTemplateOutlet=\"emptyTemplate() || defaultEmptyTemplate; context: {}\"\n ></ng-container>\n </div>\n}\n\n<ng-template #usedItemTemplate [ngnTemplate]=\"scroller.templateTypes.item\" let-node>\n @let item = node.item;\n @let disabled = node.parentDisabled || item.disabled;\n @let state = nodeState(item);\n @let selectableNode = isSelectable(node);\n <!--\n Virtual scrolling renders a flat list, so treeitems are not DOM-nested in\n role=\"group\" containers. Per WAI-ARIA, aria-level + aria-setsize +\n aria-posinset are the sanctioned substitute that convey the hierarchy to\n assistive tech. (The `group` CSS class below is visual styling only.)\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n [ngnScrollerItem]=\"node\"\n [id]=\"inputId() + '_node_' + item.value\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.level + 1\"\n [attr.aria-setsize]=\"node.setSize\"\n [attr.aria-posinset]=\"node.posInSet\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"\n !showCheckboxes() && selectableNode ? valueArray().includes(item.value) : null\n \"\n [attr.aria-checked]=\"\n showCheckboxes() && selectableNode\n ? state === 'indeterminate'\n ? 'mixed'\n : state === 'checked'\n ? 'true'\n : 'false'\n : null\n \"\n [attr.aria-disabled]=\"disabled ? 'true' : null\"\n [style.--ngn-tree-level]=\"node.level\"\n [ptInt]=\"this\"\n [ptClass]=\"{\n item: !item.items,\n group: !!item.items,\n 'item-selectable': selectableNode,\n 'item-selected': valueArray().includes(item.value),\n 'item-highlighted': item.value === currentHighlightedValue(),\n 'item-disabled': !!disabled,\n 'item-expanded': node.expanded,\n }\"\n (click)=\"onRowClick(node)\"\n >\n @if (node.hasChildren) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [ptInt]=\"this\"\n [ptClass]=\"'toggle'\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"toggleExpand(item); $event.stopPropagation()\"\n >\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-icon'\">\n @if (isLoading(item)) {\n <ngn-spinner [size]=\"14\" />\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-arrow'\"></span>\n }\n </span>\n </button>\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-placeholder'\" aria-hidden=\"true\"></span>\n }\n @if (showCheckboxes() && selectableNode) {\n <ngn-checkbox\n [ptInt]=\"this\"\n [ptDep]=\"'item-checkbox'\"\n [allowIndeterminate]=\"true\"\n [value]=\"state === 'indeterminate' ? null : state === 'checked'\"\n aria-hidden=\"true\"\n inert=\"true\"\n />\n } @else if (showCheckboxes() && selectable() && item.selectable !== false) {\n <!-- Disabled node: show the checkbox for consistency, but disabled. -->\n <ngn-checkbox\n [ptInt]=\"this\"\n [ptDep]=\"'item-checkbox'\"\n [allowIndeterminate]=\"true\"\n [value]=\"state === 'indeterminate' ? null : state === 'checked'\"\n [disabled]=\"true\"\n aria-hidden=\"true\"\n inert=\"true\"\n />\n } @else if (showCheckboxes()) {\n <span [ptInt]=\"this\" [ptClass]=\"'item-checkbox-placeholder'\" aria-hidden=\"true\"></span>\n }\n @if (item.template) {\n <ng-container *ngTemplateOutlet=\"item.template; context: { $implicit: item }\"></ng-container>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: {\n $implicit: item,\n level: node.level,\n expanded: node.expanded,\n hasChildren: node.hasChildren,\n }\n \"\n ></ng-container>\n }\n </div>\n</ng-template>\n\n<ng-template #defaultItemTemplate [ngnTemplate]=\"templateTypes.item\" let-item>\n <span [attr.data-testid]=\"item?.testId\" [ptInt]=\"this\" [ptClass]=\"'default-item'\">\n @let label = item?.label;\n @if (label) {\n <span>{{ maybeCallback(label) }}</span>\n } @else {\n <span>​</span>\n }\n </span>\n</ng-template>\n\n<ng-template #defaultEmptyTemplate>\n <span [ptInt]=\"this\" [ptClass]=\"'empty'\" [attr.data-testid]=\"'empty'\">{{\n i18n['tree_noItemsFound']()\n }}</span>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, input, booleanAttribute, output, signal, computed, DestroyRef, afterNextRender, effect, Component } from '@angular/core';
|
|
3
|
+
import { NgnBase, NgnPt, provideSelf } from '@ngneers/controls/base';
|
|
4
|
+
import { NgnButton } from '@ngneers/controls/button';
|
|
5
|
+
import { I18n } from '@ngneers/controls/i18n';
|
|
6
|
+
import { NgnIcon } from '@ngneers/controls/icon';
|
|
7
|
+
import { NgnProgress } from '@ngneers/controls/progress';
|
|
8
|
+
import { uploadControlTemplate } from '@ngneers/controls-themes/templates/upload';
|
|
9
|
+
|
|
10
|
+
let uploadFileIdSeq = 0;
|
|
11
|
+
let uploadInstanceSeq = 0;
|
|
12
|
+
/**
|
|
13
|
+
* A file drop zone that wraps a **consumer-provided** native `input[type=file]`.
|
|
14
|
+
* Project the input as content and configure its native features there
|
|
15
|
+
* (`accept`, `multiple`, `name`, size limits, …) — the control discovers it,
|
|
16
|
+
* hides it, and drives it:
|
|
17
|
+
*
|
|
18
|
+
* ```html
|
|
19
|
+
* <ngn-upload #up="ngnUpload" mode="auto" (upload)="send($event, up)">
|
|
20
|
+
* <input type="file" multiple accept="image/*" />
|
|
21
|
+
* </ngn-upload>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* The control owns the list of {@link NgnUploadFile}s and their lifecycle
|
|
25
|
+
* state, but it **cannot** observe the actual transfer (the consumer runs the
|
|
26
|
+
* request). Report progress/status back through the `exportAs="ngnUpload"`
|
|
27
|
+
* handle: {@link setProgress}, {@link markDone}, {@link markFailed}.
|
|
28
|
+
*
|
|
29
|
+
* @category control
|
|
30
|
+
*/
|
|
31
|
+
class NgnUpload extends NgnBase {
|
|
32
|
+
theme = this.injectThemeTemplate(uploadControlTemplate, {
|
|
33
|
+
root: true,
|
|
34
|
+
dragover: () => this.dragover(),
|
|
35
|
+
disabled: () => this.disabled(),
|
|
36
|
+
clickable: () => this.clickable(),
|
|
37
|
+
draggable: () => this.draggable(),
|
|
38
|
+
'has-files': () => this.files().length > 0,
|
|
39
|
+
'list-top': () => this.listPosition() === 'top',
|
|
40
|
+
'list-bottom': () => this.listPosition() === 'bottom',
|
|
41
|
+
'list-left': () => this.listPosition() === 'left',
|
|
42
|
+
'list-right': () => this.listPosition() === 'right',
|
|
43
|
+
});
|
|
44
|
+
i18n = inject(I18n).translations;
|
|
45
|
+
/** Unique id for the placeholder, referenced as the native input's a11y name. */
|
|
46
|
+
labelId = `ngn-upload-label-${++uploadInstanceSeq}`;
|
|
47
|
+
/**
|
|
48
|
+
* How the user may add files.
|
|
49
|
+
* - `click` — only clicking the zone opens the picker.
|
|
50
|
+
* - `drag` — only dropping files works.
|
|
51
|
+
* - `both` — either.
|
|
52
|
+
* @default both
|
|
53
|
+
*/
|
|
54
|
+
interaction = input('both', /* @ts-ignore */
|
|
55
|
+
...(ngDevMode ? [{ debugName: "interaction" }] : /* istanbul ignore next */ []));
|
|
56
|
+
/**
|
|
57
|
+
* When uploads start: `auto` (instantly), `confirm` (user presses a rendered
|
|
58
|
+
* trigger), or `manual` (only via {@link uploadAll}/{@link uploadFile}).
|
|
59
|
+
* @default auto
|
|
60
|
+
*/
|
|
61
|
+
mode = input('auto', /* @ts-ignore */
|
|
62
|
+
...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
63
|
+
/**
|
|
64
|
+
* Provide a custom upload icon
|
|
65
|
+
*/
|
|
66
|
+
iconUpload = input(/* @ts-ignore */
|
|
67
|
+
...(ngDevMode ? [undefined, { debugName: "iconUpload" }] : /* istanbul ignore next */ []));
|
|
68
|
+
/**
|
|
69
|
+
* In `confirm` mode, which trigger(s) to render: a single "upload all" button
|
|
70
|
+
* (`all`), a button per pending item (`per-item`), or `both`.
|
|
71
|
+
* @default all
|
|
72
|
+
*/
|
|
73
|
+
confirmTrigger = input('all', /* @ts-ignore */
|
|
74
|
+
...(ngDevMode ? [{ debugName: "confirmTrigger" }] : /* istanbul ignore next */ []));
|
|
75
|
+
/**
|
|
76
|
+
* Disables all interaction.
|
|
77
|
+
* @default false
|
|
78
|
+
*/
|
|
79
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
80
|
+
/**
|
|
81
|
+
* Where the file list renders relative to the drop zone.
|
|
82
|
+
* @default bottom
|
|
83
|
+
*/
|
|
84
|
+
listPosition = input('bottom', /* @ts-ignore */
|
|
85
|
+
...(ngDevMode ? [{ debugName: "listPosition" }] : /* istanbul ignore next */ []));
|
|
86
|
+
/**
|
|
87
|
+
* Emitted with the files to upload: on select/drop in `auto` mode, or when a
|
|
88
|
+
* manual trigger fires. The emitted items are already marked `uploading`.
|
|
89
|
+
*/
|
|
90
|
+
upload = output();
|
|
91
|
+
/** Emitted when a file is removed from the list. */
|
|
92
|
+
remove = output();
|
|
93
|
+
/** Emitted when a `failed` file's retry is pressed (also re-emits `upload`). */
|
|
94
|
+
retry = output();
|
|
95
|
+
/**
|
|
96
|
+
* Emitted when an in-flight file is dismissed (the item's remove/cancel
|
|
97
|
+
* action while `uploading`). Abort the request; the item is then removed.
|
|
98
|
+
*/
|
|
99
|
+
cancelUpload = output();
|
|
100
|
+
/** The tracked files, in insertion order. */
|
|
101
|
+
files = signal([], /* @ts-ignore */
|
|
102
|
+
...(ngDevMode ? [{ debugName: "files" }] : /* istanbul ignore next */ []));
|
|
103
|
+
dragover = signal(false, /* @ts-ignore */
|
|
104
|
+
...(ngDevMode ? [{ debugName: "dragover" }] : /* istanbul ignore next */ []));
|
|
105
|
+
clickable = computed(() => this.interaction() === 'click' || this.interaction() === 'both', /* @ts-ignore */
|
|
106
|
+
...(ngDevMode ? [{ debugName: "clickable" }] : /* istanbul ignore next */ []));
|
|
107
|
+
draggable = computed(() => this.interaction() === 'drag' || this.interaction() === 'both', /* @ts-ignore */
|
|
108
|
+
...(ngDevMode ? [{ debugName: "draggable" }] : /* istanbul ignore next */ []));
|
|
109
|
+
hasPending = computed(() => this.files().some(f => f.state === 'pending'), /* @ts-ignore */
|
|
110
|
+
...(ngDevMode ? [{ debugName: "hasPending" }] : /* istanbul ignore next */ []));
|
|
111
|
+
showUploadAll = computed(() => this.mode() === 'confirm' &&
|
|
112
|
+
(this.confirmTrigger() === 'all' || this.confirmTrigger() === 'both') &&
|
|
113
|
+
this.hasPending(), /* @ts-ignore */
|
|
114
|
+
...(ngDevMode ? [{ debugName: "showUploadAll" }] : /* istanbul ignore next */ []));
|
|
115
|
+
showPerItem = computed(() => this.mode() === 'confirm' &&
|
|
116
|
+
(this.confirmTrigger() === 'per-item' || this.confirmTrigger() === 'both'), /* @ts-ignore */
|
|
117
|
+
...(ngDevMode ? [{ debugName: "showPerItem" }] : /* istanbul ignore next */ []));
|
|
118
|
+
_nativeInput = signal(null, /* @ts-ignore */
|
|
119
|
+
...(ngDevMode ? [{ debugName: "_nativeInput" }] : /* istanbul ignore next */ []));
|
|
120
|
+
/** Resolvers awaiting a file (by id) reaching a terminal/settled state. */
|
|
121
|
+
_settlers = new Map();
|
|
122
|
+
constructor() {
|
|
123
|
+
super();
|
|
124
|
+
// Never leave an awaited upload promise dangling if we're torn down mid-flight.
|
|
125
|
+
inject(DestroyRef).onDestroy(() => {
|
|
126
|
+
for (const [id] of this._settlers) {
|
|
127
|
+
this.settle(id);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
afterNextRender(() => {
|
|
131
|
+
const root = this.element.nativeElement;
|
|
132
|
+
const input = root.querySelector('input[type=file]');
|
|
133
|
+
if (!input) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
input.addEventListener('change', () => this.onNativeChange(input));
|
|
137
|
+
// The native input is the accessible control; name it from the projected
|
|
138
|
+
// placeholder content (previously the zone's own text). Set here because
|
|
139
|
+
// the input is projected content we can't bind an attribute on directly.
|
|
140
|
+
input.setAttribute('aria-labelledby', this.labelId);
|
|
141
|
+
this._nativeInput.set(input);
|
|
142
|
+
});
|
|
143
|
+
// The projected native input[type=file] is itself the interactive, focusable
|
|
144
|
+
// control: it overlays the zone and opens the picker natively on
|
|
145
|
+
// click/Enter/Space. Drop it from the tab order only when clicking is
|
|
146
|
+
// disallowed (drag-only) or the control is disabled, so screen-reader users
|
|
147
|
+
// get a single, real focus stop with correct semantics.
|
|
148
|
+
effect(() => {
|
|
149
|
+
const input = this._nativeInput();
|
|
150
|
+
if (!input) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
input.disabled = this.disabled();
|
|
154
|
+
input.tabIndex = this.clickable() && !this.disabled() ? 0 : -1;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// --- Public handle (exportAs="ngnUpload") ----------------------------------
|
|
158
|
+
/** Report transfer progress (`0`–`100`) for a file; marks it `uploading`. */
|
|
159
|
+
setProgress(id, progress) {
|
|
160
|
+
this.patch(id, { state: 'uploading', progress: clampPercent(progress) });
|
|
161
|
+
}
|
|
162
|
+
/** Mark a file as actively uploading. */
|
|
163
|
+
markUploading(id) {
|
|
164
|
+
this.patch(id, { state: 'uploading' });
|
|
165
|
+
}
|
|
166
|
+
/** Mark a file as successfully uploaded. */
|
|
167
|
+
markDone(id) {
|
|
168
|
+
this.patch(id, { state: 'done', progress: 100 });
|
|
169
|
+
}
|
|
170
|
+
/** Mark a file as failed, optionally attaching an error. */
|
|
171
|
+
markFailed(id, error) {
|
|
172
|
+
this.patch(id, { state: 'failed', error });
|
|
173
|
+
}
|
|
174
|
+
// --- Interaction -----------------------------------------------------------
|
|
175
|
+
onDragOver(event) {
|
|
176
|
+
if (!this.draggable() || this.disabled()) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
this.dragover.set(true);
|
|
181
|
+
}
|
|
182
|
+
onDragLeave(event) {
|
|
183
|
+
// Ignore leaves bubbling from children still inside the zone.
|
|
184
|
+
if (event.relatedTarget && this.element.nativeElement.contains(event.relatedTarget)) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.dragover.set(false);
|
|
188
|
+
}
|
|
189
|
+
onDrop(event) {
|
|
190
|
+
if (!this.draggable() || this.disabled()) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
event.preventDefault();
|
|
194
|
+
this.dragover.set(false);
|
|
195
|
+
const dropped = event.dataTransfer?.files;
|
|
196
|
+
if (dropped?.length) {
|
|
197
|
+
this.ingest(Array.from(dropped), true);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
onNativeChange(input) {
|
|
201
|
+
if (input.files?.length) {
|
|
202
|
+
// Native input already enforced accept/multiple; ingest as-is.
|
|
203
|
+
this.ingest(Array.from(input.files), false);
|
|
204
|
+
}
|
|
205
|
+
// Reset so selecting the same file again re-triggers `change`.
|
|
206
|
+
input.value = '';
|
|
207
|
+
}
|
|
208
|
+
// --- Upload triggers -------------------------------------------------------
|
|
209
|
+
/**
|
|
210
|
+
* Start uploading every `pending` file. This is the code-driven trigger for
|
|
211
|
+
* `manual` mode, and what the "upload all" button calls in `confirm` mode.
|
|
212
|
+
*
|
|
213
|
+
* Returns a promise that resolves once every started file has settled — i.e.
|
|
214
|
+
* reached `done`, `failed`, or been cancelled/removed. It resolves with the
|
|
215
|
+
* same items handed to `(upload)`, so their final {@link NgnUploadFile.state}
|
|
216
|
+
* tells you which succeeded and which failed. Resolves with `[]` if there was
|
|
217
|
+
* nothing pending.
|
|
218
|
+
*/
|
|
219
|
+
uploadAll() {
|
|
220
|
+
const pending = this.files().filter(f => f.state === 'pending');
|
|
221
|
+
return pending.length ? this.startUpload(pending) : Promise.resolve([]);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Start uploading a single `pending` file by its {@link NgnUploadFile.id}.
|
|
225
|
+
* Returns the same settle-promise as {@link uploadAll}.
|
|
226
|
+
*/
|
|
227
|
+
uploadFile(id) {
|
|
228
|
+
const item = this.files().find(f => f.id === id && f.state === 'pending');
|
|
229
|
+
return item ? this.startUpload([item]) : Promise.resolve([]);
|
|
230
|
+
}
|
|
231
|
+
uploadOne(item) {
|
|
232
|
+
void this.startUpload([item]);
|
|
233
|
+
}
|
|
234
|
+
retryFile(item) {
|
|
235
|
+
this.retry.emit(item);
|
|
236
|
+
void this.startUpload([item]);
|
|
237
|
+
}
|
|
238
|
+
removeFile(item) {
|
|
239
|
+
// A single dismiss action: while in-flight, aborting the upload is implied,
|
|
240
|
+
// so tell the consumer to cancel the request before we drop the item.
|
|
241
|
+
if (item.state === 'uploading') {
|
|
242
|
+
this.cancelUpload.emit(item);
|
|
243
|
+
// Move to a terminal state before settling so uploadAll()/uploadFile()
|
|
244
|
+
// don't resolve with the item still reading as `uploading`.
|
|
245
|
+
item.state = 'cancelled';
|
|
246
|
+
}
|
|
247
|
+
this.files.update(files => files.filter(f => f.id !== item.id));
|
|
248
|
+
this.settle(item.id);
|
|
249
|
+
this.remove.emit(item);
|
|
250
|
+
}
|
|
251
|
+
/** Human-readable file size for display in the list. */
|
|
252
|
+
formatSize(bytes) {
|
|
253
|
+
if (bytes < 1024) {
|
|
254
|
+
return `${bytes} B`;
|
|
255
|
+
}
|
|
256
|
+
const units = ['KB', 'MB', 'GB', 'TB'];
|
|
257
|
+
let size = bytes / 1024;
|
|
258
|
+
let unit = 0;
|
|
259
|
+
while (size >= 1024 && unit < units.length - 1) {
|
|
260
|
+
size /= 1024;
|
|
261
|
+
unit++;
|
|
262
|
+
}
|
|
263
|
+
return `${size.toFixed(1)} ${units[unit]}`;
|
|
264
|
+
}
|
|
265
|
+
// --- Internals -------------------------------------------------------------
|
|
266
|
+
/** Filter/trim dropped files to mirror the native input's own validation. */
|
|
267
|
+
ingest(files, isDrop) {
|
|
268
|
+
const input = this._nativeInput();
|
|
269
|
+
let accepted = files;
|
|
270
|
+
if (isDrop && input) {
|
|
271
|
+
if (input.accept.trim()) {
|
|
272
|
+
accepted = accepted.filter(f => matchesAccept(f, input.accept));
|
|
273
|
+
}
|
|
274
|
+
if (!input.multiple) {
|
|
275
|
+
accepted = accepted.slice(0, 1);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!accepted.length) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const added = accepted.map(file => ({
|
|
282
|
+
id: `ngn-upload-${++uploadFileIdSeq}`,
|
|
283
|
+
file,
|
|
284
|
+
state: 'pending',
|
|
285
|
+
progress: 0,
|
|
286
|
+
}));
|
|
287
|
+
this.files.update(current => [...current, ...added]);
|
|
288
|
+
if (this.mode() === 'auto') {
|
|
289
|
+
void this.startUpload(added);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
startUpload(items) {
|
|
293
|
+
for (const item of items) {
|
|
294
|
+
item.state = 'uploading';
|
|
295
|
+
}
|
|
296
|
+
this.files.update(files => [...files]);
|
|
297
|
+
this.upload.emit(items);
|
|
298
|
+
return Promise.all(items.map(item => this.whenSettled(item.id))).then(() => items);
|
|
299
|
+
}
|
|
300
|
+
/** Resolve once the file (by id) leaves `uploading` — done/failed/cancelled. */
|
|
301
|
+
whenSettled(id) {
|
|
302
|
+
return new Promise(resolve => {
|
|
303
|
+
const item = this.files().find(f => f.id === id);
|
|
304
|
+
if (!item || item.state !== 'uploading') {
|
|
305
|
+
resolve();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const waiting = this._settlers.get(id) ?? [];
|
|
309
|
+
waiting.push(resolve);
|
|
310
|
+
this._settlers.set(id, waiting);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
settle(id) {
|
|
314
|
+
const waiting = this._settlers.get(id);
|
|
315
|
+
if (waiting) {
|
|
316
|
+
this._settlers.delete(id);
|
|
317
|
+
for (const resolve of waiting) {
|
|
318
|
+
resolve();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
patch(id, changes) {
|
|
323
|
+
this.files.update(files => {
|
|
324
|
+
const item = files.find(f => f.id === id);
|
|
325
|
+
if (item) {
|
|
326
|
+
Object.assign(item, changes);
|
|
327
|
+
}
|
|
328
|
+
return [...files];
|
|
329
|
+
});
|
|
330
|
+
// Any transition out of `uploading` settles a pending upload promise.
|
|
331
|
+
if (changes.state && changes.state !== 'uploading') {
|
|
332
|
+
this.settle(id);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnUpload, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
336
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnUpload, isStandalone: true, selector: "ngn-upload", inputs: { interaction: { classPropertyName: "interaction", publicName: "interaction", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, iconUpload: { classPropertyName: "iconUpload", publicName: "iconUpload", isSignal: true, isRequired: false, transformFunction: null }, confirmTrigger: { classPropertyName: "confirmTrigger", publicName: "confirmTrigger", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, listPosition: { classPropertyName: "listPosition", publicName: "listPosition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { upload: "upload", remove: "remove", retry: "retry", cancelUpload: "cancelUpload" }, host: { listeners: { "dragenter": "onDragOver($event)", "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "drop": "onDrop($event)" } }, providers: [provideSelf(NgnUpload)], exportAs: ["ngnUpload"], usesInheritance: true, ngImport: i0, template: "<div [ptInt]=\"this\" [ptClass]=\"'main'\">\n <div [ptInt]=\"this\" [ptClass]=\"'zone'\">\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'icon'\"\n [defaultIcon]=\"'upload-trigger'\"\n [icon]=\"iconUpload()\"\n />\n <!-- The projected native input[type=file] is the real, focusable control:\n it overlays the zone (see the 'native' theme part) and opens the picker\n natively on click/Enter/Space. Keep it accessible (no aria-hidden). -->\n <div [ptInt]=\"this\" [ptClass]=\"'placeholder'\" [id]=\"labelId\"><ng-content /></div>\n </div>\n\n @if (showUploadAll()) {\n <div [ptInt]=\"this\" [ptClass]=\"'footer'\">\n <button\n ngnButton\n kind=\"primary\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'trigger'\"\n (click)=\"uploadAll()\"\n >\n <ngn-icon [defaultIcon]=\"'upload-trigger'\" />\n {{ i18n['upload_upload']() }}\n </button>\n </div>\n }\n</div>\n\n@if (files().length) {\n <ul [ptInt]=\"this\" [ptClass]=\"'list'\">\n @for (item of files(); track item.id) {\n <li\n [ptInt]=\"this\"\n [ptClass]=\"{\n item: true,\n 'item-pending': item.state === 'pending',\n 'item-uploading': item.state === 'uploading',\n 'item-done': item.state === 'done',\n 'item-failed': item.state === 'failed',\n }\"\n >\n @if (item.state === 'done' || item.state === 'failed') {\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'status'\"\n [defaultIcon]=\"item.state === 'failed' ? 'upload-failed' : 'upload-done'\"\n />\n }\n\n <div [ptInt]=\"this\" [ptClass]=\"'file'\">\n <span [ptInt]=\"this\" [ptClass]=\"'name'\" [title]=\"item.file.name\">{{\n item.file.name\n }}</span>\n <span [ptInt]=\"this\" [ptClass]=\"'size'\">{{ formatSize(item.file.size) }}</span>\n @if (item.state === 'uploading') {\n <ngn-progress\n [ptInt]=\"this\"\n [ptClass]=\"'progress'\"\n [attr.aria-label]=\"i18n['upload_progressNamed']({ name: item.file.name })\"\n [value]=\"item.progress\"\n [indeterminate]=\"item.progress === 0\"\n />\n }\n </div>\n\n <div [ptInt]=\"this\" [ptClass]=\"'actions'\">\n @if (showPerItem() && item.state === 'pending') {\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"i18n['upload_uploadNamed']({ name: item.file.name })\"\n (click)=\"uploadOne(item)\"\n >\n <ngn-icon [defaultIcon]=\"'upload-trigger'\" />\n </button>\n }\n @if (item.state === 'failed') {\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"i18n['upload_retryNamed']({ name: item.file.name })\"\n (click)=\"retryFile(item)\"\n >\n <ngn-icon [defaultIcon]=\"'upload-retry'\" />\n </button>\n }\n <!-- Single dismiss action: cancels the in-flight upload and removes the\n item while uploading, otherwise just removes it. -->\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"\n item.state === 'uploading'\n ? i18n['upload_cancelNamed']({ name: item.file.name })\n : i18n['upload_removeNamed']({ name: item.file.name })\n \"\n (click)=\"removeFile(item)\"\n >\n <ngn-icon\n [defaultIcon]=\"item.state === 'uploading' ? 'upload-cancel' : 'upload-remove'\"\n />\n </button>\n </div>\n </li>\n }\n </ul>\n}\n", dependencies: [{ kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }, { kind: "directive", type: NgnButton, selector: "button[ngnButton], a[ngnButton]", inputs: ["ngnButtonInline"], exportAs: ["ngnButton"] }, { kind: "component", type: NgnProgress, selector: "ngn-progress", inputs: ["value", "indeterminate", "circular", "radius", "thickness"] }] });
|
|
337
|
+
}
|
|
338
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnUpload, decorators: [{
|
|
339
|
+
type: Component,
|
|
340
|
+
args: [{ selector: 'ngn-upload', exportAs: 'ngnUpload', imports: [NgnPt, NgnIcon, NgnButton, NgnProgress], providers: [provideSelf(NgnUpload)], host: {
|
|
341
|
+
'(dragenter)': 'onDragOver($event)',
|
|
342
|
+
'(dragover)': 'onDragOver($event)',
|
|
343
|
+
'(dragleave)': 'onDragLeave($event)',
|
|
344
|
+
'(drop)': 'onDrop($event)',
|
|
345
|
+
}, template: "<div [ptInt]=\"this\" [ptClass]=\"'main'\">\n <div [ptInt]=\"this\" [ptClass]=\"'zone'\">\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'icon'\"\n [defaultIcon]=\"'upload-trigger'\"\n [icon]=\"iconUpload()\"\n />\n <!-- The projected native input[type=file] is the real, focusable control:\n it overlays the zone (see the 'native' theme part) and opens the picker\n natively on click/Enter/Space. Keep it accessible (no aria-hidden). -->\n <div [ptInt]=\"this\" [ptClass]=\"'placeholder'\" [id]=\"labelId\"><ng-content /></div>\n </div>\n\n @if (showUploadAll()) {\n <div [ptInt]=\"this\" [ptClass]=\"'footer'\">\n <button\n ngnButton\n kind=\"primary\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'trigger'\"\n (click)=\"uploadAll()\"\n >\n <ngn-icon [defaultIcon]=\"'upload-trigger'\" />\n {{ i18n['upload_upload']() }}\n </button>\n </div>\n }\n</div>\n\n@if (files().length) {\n <ul [ptInt]=\"this\" [ptClass]=\"'list'\">\n @for (item of files(); track item.id) {\n <li\n [ptInt]=\"this\"\n [ptClass]=\"{\n item: true,\n 'item-pending': item.state === 'pending',\n 'item-uploading': item.state === 'uploading',\n 'item-done': item.state === 'done',\n 'item-failed': item.state === 'failed',\n }\"\n >\n @if (item.state === 'done' || item.state === 'failed') {\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'status'\"\n [defaultIcon]=\"item.state === 'failed' ? 'upload-failed' : 'upload-done'\"\n />\n }\n\n <div [ptInt]=\"this\" [ptClass]=\"'file'\">\n <span [ptInt]=\"this\" [ptClass]=\"'name'\" [title]=\"item.file.name\">{{\n item.file.name\n }}</span>\n <span [ptInt]=\"this\" [ptClass]=\"'size'\">{{ formatSize(item.file.size) }}</span>\n @if (item.state === 'uploading') {\n <ngn-progress\n [ptInt]=\"this\"\n [ptClass]=\"'progress'\"\n [attr.aria-label]=\"i18n['upload_progressNamed']({ name: item.file.name })\"\n [value]=\"item.progress\"\n [indeterminate]=\"item.progress === 0\"\n />\n }\n </div>\n\n <div [ptInt]=\"this\" [ptClass]=\"'actions'\">\n @if (showPerItem() && item.state === 'pending') {\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"i18n['upload_uploadNamed']({ name: item.file.name })\"\n (click)=\"uploadOne(item)\"\n >\n <ngn-icon [defaultIcon]=\"'upload-trigger'\" />\n </button>\n }\n @if (item.state === 'failed') {\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"i18n['upload_retryNamed']({ name: item.file.name })\"\n (click)=\"retryFile(item)\"\n >\n <ngn-icon [defaultIcon]=\"'upload-retry'\" />\n </button>\n }\n <!-- Single dismiss action: cancels the in-flight upload and removes the\n item while uploading, otherwise just removes it. -->\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"\n item.state === 'uploading'\n ? i18n['upload_cancelNamed']({ name: item.file.name })\n : i18n['upload_removeNamed']({ name: item.file.name })\n \"\n (click)=\"removeFile(item)\"\n >\n <ngn-icon\n [defaultIcon]=\"item.state === 'uploading' ? 'upload-cancel' : 'upload-remove'\"\n />\n </button>\n </div>\n </li>\n }\n </ul>\n}\n" }]
|
|
346
|
+
}], ctorParameters: () => [], propDecorators: { interaction: [{ type: i0.Input, args: [{ isSignal: true, alias: "interaction", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], iconUpload: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconUpload", required: false }] }], confirmTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmTrigger", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], listPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "listPosition", required: false }] }], upload: [{ type: i0.Output, args: ["upload"] }], remove: [{ type: i0.Output, args: ["remove"] }], retry: [{ type: i0.Output, args: ["retry"] }], cancelUpload: [{ type: i0.Output, args: ["cancelUpload"] }] } });
|
|
347
|
+
function clampPercent(value) {
|
|
348
|
+
return Math.min(Math.max(value, 0), 100);
|
|
349
|
+
}
|
|
350
|
+
/** Match a file against an `accept` attribute (`.ext`, `type/sub`, `type/*`). */
|
|
351
|
+
function matchesAccept(file, accept) {
|
|
352
|
+
const tokens = accept
|
|
353
|
+
.split(',')
|
|
354
|
+
.map(token => token.trim().toLowerCase())
|
|
355
|
+
.filter(Boolean);
|
|
356
|
+
if (!tokens.length) {
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
const name = file.name.toLowerCase();
|
|
360
|
+
const type = file.type.toLowerCase();
|
|
361
|
+
return tokens.some(token => {
|
|
362
|
+
if (token.startsWith('.')) {
|
|
363
|
+
return name.endsWith(token);
|
|
364
|
+
}
|
|
365
|
+
if (token.endsWith('/*')) {
|
|
366
|
+
return type.startsWith(token.slice(0, -1));
|
|
367
|
+
}
|
|
368
|
+
return type === token;
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Generated bundle index. Do not edit.
|
|
374
|
+
*/
|
|
375
|
+
|
|
376
|
+
export { NgnUpload };
|
|
377
|
+
//# sourceMappingURL=ngneers-controls-upload.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngneers-controls-upload.mjs","sources":["../../src/upload/upload.ts","../../src/upload/upload.html","../../src/upload/ngneers-controls-upload.ts"],"sourcesContent":["import {\n afterNextRender,\n booleanAttribute,\n Component,\n computed,\n DestroyRef,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { NgnBase, NgnPt, provideSelf } from '@ngneers/controls/base';\nimport { NgnButton } from '@ngneers/controls/button';\nimport { I18n } from '@ngneers/controls/i18n';\nimport { NgnIcon } from '@ngneers/controls/icon';\nimport { NgnProgress } from '@ngneers/controls/progress';\nimport { uploadControlTemplate } from '@ngneers/controls-themes/templates/upload';\n\nimport type { NgnUploadFile } from './types';\nimport type { IconType } from '@ngneers/controls-custom-types';\n\n/** How the user is allowed to add files to the drop zone. */\nexport type NgnUploadInteraction = 'click' | 'drag' | 'both';\n/**\n * When selected/dropped files start uploading.\n * - `auto` — instantly.\n * - `confirm` — queued as `pending`; the user presses a rendered trigger.\n * - `manual` — queued as `pending`; no trigger is rendered, upload starts only\n * via {@link NgnUpload.uploadAll}/{@link NgnUpload.uploadFile} from code.\n */\nexport type NgnUploadMode = 'auto' | 'confirm' | 'manual';\n/** Which trigger(s) are rendered in `confirm` mode. */\nexport type NgnUploadConfirmTrigger = 'all' | 'per-item' | 'both';\n/** Where the file list renders relative to the drop zone. */\nexport type NgnUploadListPosition = 'top' | 'bottom' | 'left' | 'right';\n\nlet uploadFileIdSeq = 0;\nlet uploadInstanceSeq = 0;\n\n/**\n * A file drop zone that wraps a **consumer-provided** native `input[type=file]`.\n * Project the input as content and configure its native features there\n * (`accept`, `multiple`, `name`, size limits, …) — the control discovers it,\n * hides it, and drives it:\n *\n * ```html\n * <ngn-upload #up=\"ngnUpload\" mode=\"auto\" (upload)=\"send($event, up)\">\n * <input type=\"file\" multiple accept=\"image/*\" />\n * </ngn-upload>\n * ```\n *\n * The control owns the list of {@link NgnUploadFile}s and their lifecycle\n * state, but it **cannot** observe the actual transfer (the consumer runs the\n * request). Report progress/status back through the `exportAs=\"ngnUpload\"`\n * handle: {@link setProgress}, {@link markDone}, {@link markFailed}.\n *\n * @category control\n */\n@Component({\n selector: 'ngn-upload',\n exportAs: 'ngnUpload',\n templateUrl: './upload.html',\n imports: [NgnPt, NgnIcon, NgnButton, NgnProgress],\n providers: [provideSelf(NgnUpload)],\n host: {\n '(dragenter)': 'onDragOver($event)',\n '(dragover)': 'onDragOver($event)',\n '(dragleave)': 'onDragLeave($event)',\n '(drop)': 'onDrop($event)',\n },\n})\nexport class NgnUpload extends NgnBase<'upload'> {\n protected readonly theme = this.injectThemeTemplate(uploadControlTemplate, {\n root: true,\n dragover: () => this.dragover(),\n disabled: () => this.disabled(),\n clickable: () => this.clickable(),\n draggable: () => this.draggable(),\n 'has-files': () => this.files().length > 0,\n 'list-top': () => this.listPosition() === 'top',\n 'list-bottom': () => this.listPosition() === 'bottom',\n 'list-left': () => this.listPosition() === 'left',\n 'list-right': () => this.listPosition() === 'right',\n });\n\n protected readonly i18n = inject(I18n).translations;\n\n /** Unique id for the placeholder, referenced as the native input's a11y name. */\n protected readonly labelId = `ngn-upload-label-${++uploadInstanceSeq}`;\n\n /**\n * How the user may add files.\n * - `click` — only clicking the zone opens the picker.\n * - `drag` — only dropping files works.\n * - `both` — either.\n * @default both\n */\n public readonly interaction = input<NgnUploadInteraction>('both');\n /**\n * When uploads start: `auto` (instantly), `confirm` (user presses a rendered\n * trigger), or `manual` (only via {@link uploadAll}/{@link uploadFile}).\n * @default auto\n */\n public readonly mode = input<NgnUploadMode>('auto');\n /**\n * Provide a custom upload icon\n */\n public readonly iconUpload = input<IconType>();\n /**\n * In `confirm` mode, which trigger(s) to render: a single \"upload all\" button\n * (`all`), a button per pending item (`per-item`), or `both`.\n * @default all\n */\n public readonly confirmTrigger = input<NgnUploadConfirmTrigger>('all');\n /**\n * Disables all interaction.\n * @default false\n */\n public readonly disabled = input(false, { transform: booleanAttribute });\n /**\n * Where the file list renders relative to the drop zone.\n * @default bottom\n */\n public readonly listPosition = input<NgnUploadListPosition>('bottom');\n\n /**\n * Emitted with the files to upload: on select/drop in `auto` mode, or when a\n * manual trigger fires. The emitted items are already marked `uploading`.\n */\n public readonly upload = output<NgnUploadFile[]>();\n /** Emitted when a file is removed from the list. */\n public readonly remove = output<NgnUploadFile>();\n /** Emitted when a `failed` file's retry is pressed (also re-emits `upload`). */\n public readonly retry = output<NgnUploadFile>();\n /**\n * Emitted when an in-flight file is dismissed (the item's remove/cancel\n * action while `uploading`). Abort the request; the item is then removed.\n */\n public readonly cancelUpload = output<NgnUploadFile>();\n\n /** The tracked files, in insertion order. */\n public readonly files = signal<NgnUploadFile[]>([]);\n\n protected readonly dragover = signal(false);\n\n protected readonly clickable = computed(\n () => this.interaction() === 'click' || this.interaction() === 'both'\n );\n protected readonly draggable = computed(\n () => this.interaction() === 'drag' || this.interaction() === 'both'\n );\n\n protected readonly hasPending = computed(() => this.files().some(f => f.state === 'pending'));\n protected readonly showUploadAll = computed(\n () =>\n this.mode() === 'confirm' &&\n (this.confirmTrigger() === 'all' || this.confirmTrigger() === 'both') &&\n this.hasPending()\n );\n protected readonly showPerItem = computed(\n () =>\n this.mode() === 'confirm' &&\n (this.confirmTrigger() === 'per-item' || this.confirmTrigger() === 'both')\n );\n\n private readonly _nativeInput = signal<HTMLInputElement | null>(null);\n\n /** Resolvers awaiting a file (by id) reaching a terminal/settled state. */\n private readonly _settlers = new Map<string, Array<() => void>>();\n\n constructor() {\n super();\n\n // Never leave an awaited upload promise dangling if we're torn down mid-flight.\n inject(DestroyRef).onDestroy(() => {\n for (const [id] of this._settlers) {\n this.settle(id);\n }\n });\n\n afterNextRender(() => {\n const root = this.element.nativeElement;\n const input = root.querySelector<HTMLInputElement>('input[type=file]');\n if (!input) {\n return;\n }\n input.addEventListener('change', () => this.onNativeChange(input));\n // The native input is the accessible control; name it from the projected\n // placeholder content (previously the zone's own text). Set here because\n // the input is projected content we can't bind an attribute on directly.\n input.setAttribute('aria-labelledby', this.labelId);\n this._nativeInput.set(input);\n });\n\n // The projected native input[type=file] is itself the interactive, focusable\n // control: it overlays the zone and opens the picker natively on\n // click/Enter/Space. Drop it from the tab order only when clicking is\n // disallowed (drag-only) or the control is disabled, so screen-reader users\n // get a single, real focus stop with correct semantics.\n effect(() => {\n const input = this._nativeInput();\n if (!input) {\n return;\n }\n input.disabled = this.disabled();\n input.tabIndex = this.clickable() && !this.disabled() ? 0 : -1;\n });\n }\n\n // --- Public handle (exportAs=\"ngnUpload\") ----------------------------------\n\n /** Report transfer progress (`0`–`100`) for a file; marks it `uploading`. */\n public setProgress(id: string, progress: number): void {\n this.patch(id, { state: 'uploading', progress: clampPercent(progress) });\n }\n\n /** Mark a file as actively uploading. */\n public markUploading(id: string): void {\n this.patch(id, { state: 'uploading' });\n }\n\n /** Mark a file as successfully uploaded. */\n public markDone(id: string): void {\n this.patch(id, { state: 'done', progress: 100 });\n }\n\n /** Mark a file as failed, optionally attaching an error. */\n public markFailed(id: string, error?: unknown): void {\n this.patch(id, { state: 'failed', error });\n }\n\n // --- Interaction -----------------------------------------------------------\n\n protected onDragOver(event: DragEvent): void {\n if (!this.draggable() || this.disabled()) {\n return;\n }\n event.preventDefault();\n this.dragover.set(true);\n }\n\n protected onDragLeave(event: DragEvent): void {\n // Ignore leaves bubbling from children still inside the zone.\n if (event.relatedTarget && this.element.nativeElement.contains(event.relatedTarget as Node)) {\n return;\n }\n this.dragover.set(false);\n }\n\n protected onDrop(event: DragEvent): void {\n if (!this.draggable() || this.disabled()) {\n return;\n }\n event.preventDefault();\n this.dragover.set(false);\n const dropped = event.dataTransfer?.files;\n if (dropped?.length) {\n this.ingest(Array.from(dropped), true);\n }\n }\n\n private onNativeChange(input: HTMLInputElement): void {\n if (input.files?.length) {\n // Native input already enforced accept/multiple; ingest as-is.\n this.ingest(Array.from(input.files), false);\n }\n // Reset so selecting the same file again re-triggers `change`.\n input.value = '';\n }\n\n // --- Upload triggers -------------------------------------------------------\n\n /**\n * Start uploading every `pending` file. This is the code-driven trigger for\n * `manual` mode, and what the \"upload all\" button calls in `confirm` mode.\n *\n * Returns a promise that resolves once every started file has settled — i.e.\n * reached `done`, `failed`, or been cancelled/removed. It resolves with the\n * same items handed to `(upload)`, so their final {@link NgnUploadFile.state}\n * tells you which succeeded and which failed. Resolves with `[]` if there was\n * nothing pending.\n */\n public uploadAll(): Promise<NgnUploadFile[]> {\n const pending = this.files().filter(f => f.state === 'pending');\n return pending.length ? this.startUpload(pending) : Promise.resolve([]);\n }\n\n /**\n * Start uploading a single `pending` file by its {@link NgnUploadFile.id}.\n * Returns the same settle-promise as {@link uploadAll}.\n */\n public uploadFile(id: string): Promise<NgnUploadFile[]> {\n const item = this.files().find(f => f.id === id && f.state === 'pending');\n return item ? this.startUpload([item]) : Promise.resolve([]);\n }\n\n protected uploadOne(item: NgnUploadFile): void {\n void this.startUpload([item]);\n }\n\n protected retryFile(item: NgnUploadFile): void {\n this.retry.emit(item);\n void this.startUpload([item]);\n }\n\n protected removeFile(item: NgnUploadFile): void {\n // A single dismiss action: while in-flight, aborting the upload is implied,\n // so tell the consumer to cancel the request before we drop the item.\n if (item.state === 'uploading') {\n this.cancelUpload.emit(item);\n // Move to a terminal state before settling so uploadAll()/uploadFile()\n // don't resolve with the item still reading as `uploading`.\n item.state = 'cancelled';\n }\n this.files.update(files => files.filter(f => f.id !== item.id));\n this.settle(item.id);\n this.remove.emit(item);\n }\n\n /** Human-readable file size for display in the list. */\n protected formatSize(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n const units = ['KB', 'MB', 'GB', 'TB'];\n let size = bytes / 1024;\n let unit = 0;\n while (size >= 1024 && unit < units.length - 1) {\n size /= 1024;\n unit++;\n }\n return `${size.toFixed(1)} ${units[unit]}`;\n }\n\n // --- Internals -------------------------------------------------------------\n\n /** Filter/trim dropped files to mirror the native input's own validation. */\n private ingest(files: File[], isDrop: boolean): void {\n const input = this._nativeInput();\n let accepted = files;\n\n if (isDrop && input) {\n if (input.accept.trim()) {\n accepted = accepted.filter(f => matchesAccept(f, input.accept));\n }\n if (!input.multiple) {\n accepted = accepted.slice(0, 1);\n }\n }\n\n if (!accepted.length) {\n return;\n }\n\n const added = accepted.map<NgnUploadFile>(file => ({\n id: `ngn-upload-${++uploadFileIdSeq}`,\n file,\n state: 'pending',\n progress: 0,\n }));\n this.files.update(current => [...current, ...added]);\n\n if (this.mode() === 'auto') {\n void this.startUpload(added);\n }\n }\n\n private startUpload(items: NgnUploadFile[]): Promise<NgnUploadFile[]> {\n for (const item of items) {\n item.state = 'uploading';\n }\n this.files.update(files => [...files]);\n this.upload.emit(items);\n return Promise.all(items.map(item => this.whenSettled(item.id))).then(() => items);\n }\n\n /** Resolve once the file (by id) leaves `uploading` — done/failed/cancelled. */\n private whenSettled(id: string): Promise<void> {\n return new Promise<void>(resolve => {\n const item = this.files().find(f => f.id === id);\n if (!item || item.state !== 'uploading') {\n resolve();\n return;\n }\n const waiting = this._settlers.get(id) ?? [];\n waiting.push(resolve);\n this._settlers.set(id, waiting);\n });\n }\n\n private settle(id: string): void {\n const waiting = this._settlers.get(id);\n if (waiting) {\n this._settlers.delete(id);\n for (const resolve of waiting) {\n resolve();\n }\n }\n }\n\n private patch(id: string, changes: Partial<NgnUploadFile>): void {\n this.files.update(files => {\n const item = files.find(f => f.id === id);\n if (item) {\n Object.assign(item, changes);\n }\n return [...files];\n });\n // Any transition out of `uploading` settles a pending upload promise.\n if (changes.state && changes.state !== 'uploading') {\n this.settle(id);\n }\n }\n}\n\nfunction clampPercent(value: number): number {\n return Math.min(Math.max(value, 0), 100);\n}\n\n/** Match a file against an `accept` attribute (`.ext`, `type/sub`, `type/*`). */\nfunction matchesAccept(file: File, accept: string): boolean {\n const tokens = accept\n .split(',')\n .map(token => token.trim().toLowerCase())\n .filter(Boolean);\n if (!tokens.length) {\n return true;\n }\n const name = file.name.toLowerCase();\n const type = file.type.toLowerCase();\n return tokens.some(token => {\n if (token.startsWith('.')) {\n return name.endsWith(token);\n }\n if (token.endsWith('/*')) {\n return type.startsWith(token.slice(0, -1));\n }\n return type === token;\n });\n}\n","<div [ptInt]=\"this\" [ptClass]=\"'main'\">\n <div [ptInt]=\"this\" [ptClass]=\"'zone'\">\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'icon'\"\n [defaultIcon]=\"'upload-trigger'\"\n [icon]=\"iconUpload()\"\n />\n <!-- The projected native input[type=file] is the real, focusable control:\n it overlays the zone (see the 'native' theme part) and opens the picker\n natively on click/Enter/Space. Keep it accessible (no aria-hidden). -->\n <div [ptInt]=\"this\" [ptClass]=\"'placeholder'\" [id]=\"labelId\"><ng-content /></div>\n </div>\n\n @if (showUploadAll()) {\n <div [ptInt]=\"this\" [ptClass]=\"'footer'\">\n <button\n ngnButton\n kind=\"primary\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'trigger'\"\n (click)=\"uploadAll()\"\n >\n <ngn-icon [defaultIcon]=\"'upload-trigger'\" />\n {{ i18n['upload_upload']() }}\n </button>\n </div>\n }\n</div>\n\n@if (files().length) {\n <ul [ptInt]=\"this\" [ptClass]=\"'list'\">\n @for (item of files(); track item.id) {\n <li\n [ptInt]=\"this\"\n [ptClass]=\"{\n item: true,\n 'item-pending': item.state === 'pending',\n 'item-uploading': item.state === 'uploading',\n 'item-done': item.state === 'done',\n 'item-failed': item.state === 'failed',\n }\"\n >\n @if (item.state === 'done' || item.state === 'failed') {\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'status'\"\n [defaultIcon]=\"item.state === 'failed' ? 'upload-failed' : 'upload-done'\"\n />\n }\n\n <div [ptInt]=\"this\" [ptClass]=\"'file'\">\n <span [ptInt]=\"this\" [ptClass]=\"'name'\" [title]=\"item.file.name\">{{\n item.file.name\n }}</span>\n <span [ptInt]=\"this\" [ptClass]=\"'size'\">{{ formatSize(item.file.size) }}</span>\n @if (item.state === 'uploading') {\n <ngn-progress\n [ptInt]=\"this\"\n [ptClass]=\"'progress'\"\n [attr.aria-label]=\"i18n['upload_progressNamed']({ name: item.file.name })\"\n [value]=\"item.progress\"\n [indeterminate]=\"item.progress === 0\"\n />\n }\n </div>\n\n <div [ptInt]=\"this\" [ptClass]=\"'actions'\">\n @if (showPerItem() && item.state === 'pending') {\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"i18n['upload_uploadNamed']({ name: item.file.name })\"\n (click)=\"uploadOne(item)\"\n >\n <ngn-icon [defaultIcon]=\"'upload-trigger'\" />\n </button>\n }\n @if (item.state === 'failed') {\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"i18n['upload_retryNamed']({ name: item.file.name })\"\n (click)=\"retryFile(item)\"\n >\n <ngn-icon [defaultIcon]=\"'upload-retry'\" />\n </button>\n }\n <!-- Single dismiss action: cancels the in-flight upload and removes the\n item while uploading, otherwise just removes it. -->\n <button\n ngnButton\n kind=\"text\"\n type=\"button\"\n [ptInt]=\"this\"\n [ptClass]=\"'action'\"\n [attr.aria-label]=\"\n item.state === 'uploading'\n ? i18n['upload_cancelNamed']({ name: item.file.name })\n : i18n['upload_removeNamed']({ name: item.file.name })\n \"\n (click)=\"removeFile(item)\"\n >\n <ngn-icon\n [defaultIcon]=\"item.state === 'uploading' ? 'upload-cancel' : 'upload-remove'\"\n />\n </button>\n </div>\n </li>\n }\n </ul>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|