@magmonium/one 0.4.30 → 0.4.32
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/assets/button_groups/nav_user_actions.json +1 -0
- package/assets/button_groups/notification_filter.json +2 -13
- package/assets/buttons/nav_user_edit_profile.json +5 -0
- package/assets/docs/en/app-release.md +0 -11
- package/assets/i18n/af.json +10 -2
- package/assets/i18n/am.json +10 -1
- package/assets/i18n/ar.json +10 -2
- package/assets/i18n/as.json +11 -2
- package/assets/i18n/az.json +11 -2
- package/assets/i18n/ba.json +8 -0
- package/assets/i18n/be.json +11 -2
- package/assets/i18n/bg.json +11 -2
- package/assets/i18n/bn.json +11 -2
- package/assets/i18n/br.json +7 -0
- package/assets/i18n/bs.json +10 -2
- package/assets/i18n/ca.json +10 -2
- package/assets/i18n/ceb.json +9 -2
- package/assets/i18n/cnr.json +8 -0
- package/assets/i18n/co.json +7 -1
- package/assets/i18n/cs.json +9 -2
- package/assets/i18n/cy.json +11 -2
- package/assets/i18n/da.json +7 -1
- package/assets/i18n/de.json +8 -2
- package/assets/i18n/doi.json +9 -0
- package/assets/i18n/el.json +10 -2
- package/assets/i18n/en.json +21 -4
- package/assets/i18n/eo.json +9 -2
- package/assets/i18n/es.json +9 -2
- package/assets/i18n/et.json +8 -2
- package/assets/i18n/eu.json +9 -2
- package/assets/i18n/fa.json +10 -2
- package/assets/i18n/fi.json +9 -2
- package/assets/i18n/fr.json +9 -2
- package/assets/i18n/fy.json +6 -2
- package/assets/i18n/ga.json +10 -2
- package/assets/i18n/gd.json +9 -2
- package/assets/i18n/gl.json +8 -1
- package/assets/i18n/gu.json +11 -2
- package/assets/i18n/ha.json +7 -1
- package/assets/i18n/haw.json +8 -2
- package/assets/i18n/he.json +8 -0
- package/assets/i18n/hi.json +10 -2
- package/assets/i18n/hmn.json +6 -2
- package/assets/i18n/hr.json +10 -2
- package/assets/i18n/hu.json +10 -2
- package/assets/i18n/hy.json +11 -2
- package/assets/i18n/id.json +10 -2
- package/assets/i18n/ig.json +8 -1
- package/assets/i18n/ilo.json +6 -0
- package/assets/i18n/is.json +10 -2
- package/assets/i18n/it.json +9 -2
- package/assets/i18n/iw.json +9 -1
- package/assets/i18n/ja.json +8 -1
- package/assets/i18n/jw.json +8 -2
- package/assets/i18n/ka.json +10 -2
- package/assets/i18n/kk.json +10 -2
- package/assets/i18n/km.json +10 -2
- package/assets/i18n/kn.json +11 -2
- package/assets/i18n/ko.json +8 -1
- package/assets/i18n/kok.json +9 -0
- package/assets/i18n/ku.json +10 -2
- package/assets/i18n/ky.json +9 -1
- package/assets/i18n/la.json +6 -1
- package/assets/i18n/lb.json +6 -2
- package/assets/i18n/lo.json +10 -2
- package/assets/i18n/lt.json +10 -2
- package/assets/i18n/lv.json +10 -2
- package/assets/i18n/mai.json +9 -0
- package/assets/i18n/mg.json +8 -2
- package/assets/i18n/mi.json +8 -2
- package/assets/i18n/mk.json +10 -2
- package/assets/i18n/ml.json +11 -2
- package/assets/i18n/mn.json +10 -2
- package/assets/i18n/mr.json +11 -2
- package/assets/i18n/ms.json +9 -2
- package/assets/i18n/mt.json +9 -2
- package/assets/i18n/my.json +10 -2
- package/assets/i18n/ne.json +10 -2
- package/assets/i18n/nl.json +6 -2
- package/assets/i18n/no.json +8 -2
- package/assets/i18n/ny.json +9 -2
- package/assets/i18n/or.json +10 -1
- package/assets/i18n/pa.json +11 -2
- package/assets/i18n/pl.json +8 -2
- package/assets/i18n/ps.json +8 -1
- package/assets/i18n/pt.json +9 -2
- package/assets/i18n/ro.json +9 -2
- package/assets/i18n/ru.json +9 -2
- package/assets/i18n/sa.json +9 -0
- package/assets/i18n/sat.json +9 -0
- package/assets/i18n/sd.json +11 -2
- package/assets/i18n/si.json +10 -2
- package/assets/i18n/sk.json +6 -1
- package/assets/i18n/sl.json +10 -2
- package/assets/i18n/sm.json +7 -1
- package/assets/i18n/sn.json +6 -2
- package/assets/i18n/so.json +8 -1
- package/assets/i18n/sq.json +10 -2
- package/assets/i18n/sr.json +10 -2
- package/assets/i18n/st.json +8 -2
- package/assets/i18n/su.json +8 -2
- package/assets/i18n/sv.json +8 -2
- package/assets/i18n/sw.json +10 -2
- package/assets/i18n/ta.json +11 -2
- package/assets/i18n/te.json +11 -2
- package/assets/i18n/tg.json +11 -2
- package/assets/i18n/th.json +10 -1
- package/assets/i18n/tl.json +8 -2
- package/assets/i18n/tr.json +10 -2
- package/assets/i18n/uk.json +11 -2
- package/assets/i18n/ur.json +11 -2
- package/assets/i18n/uz.json +10 -2
- package/assets/i18n/vi.json +10 -1
- package/assets/i18n/xh.json +11 -2
- package/assets/i18n/yi.json +10 -1
- package/assets/i18n/yo.json +9 -1
- package/assets/i18n/zh.json +9 -1
- package/assets/i18n/zu.json +11 -2
- package/assets/index.json +1 -1
- package/fesm2022/magmonium-one-attribute-value-DMI0yJpL.mjs +255 -0
- package/fesm2022/magmonium-one-attribute-value-DMI0yJpL.mjs.map +1 -0
- package/fesm2022/magmonium-one-base-checkbox-C9dvZ2IH.mjs +77 -0
- package/fesm2022/magmonium-one-base-checkbox-C9dvZ2IH.mjs.map +1 -0
- package/fesm2022/magmonium-one-base-dropdown-U-cWl3p9.mjs +604 -0
- package/fesm2022/magmonium-one-base-dropdown-U-cWl3p9.mjs.map +1 -0
- package/fesm2022/magmonium-one-base-input-CLBOTOUs.mjs +183 -0
- package/fesm2022/magmonium-one-base-input-CLBOTOUs.mjs.map +1 -0
- package/fesm2022/magmonium-one-base-text-Zb0oLNRX.mjs +94 -0
- package/fesm2022/magmonium-one-base-text-Zb0oLNRX.mjs.map +1 -0
- package/fesm2022/magmonium-one-button-Bwzaxxn3.mjs +1281 -0
- package/fesm2022/magmonium-one-button-Bwzaxxn3.mjs.map +1 -0
- package/fesm2022/magmonium-one-checkbox-CPQ9m291.mjs +365 -0
- package/fesm2022/magmonium-one-checkbox-CPQ9m291.mjs.map +1 -0
- package/fesm2022/magmonium-one-color-utl-CAbZLKB5.mjs +449 -0
- package/fesm2022/magmonium-one-color-utl-CAbZLKB5.mjs.map +1 -0
- package/fesm2022/magmonium-one-directives-BVKX77zM.mjs +1285 -0
- package/fesm2022/magmonium-one-directives-BVKX77zM.mjs.map +1 -0
- package/fesm2022/magmonium-one-file.service-Bo06pU3_.mjs +65 -0
- package/fesm2022/magmonium-one-file.service-Bo06pU3_.mjs.map +1 -0
- package/fesm2022/magmonium-one-group-D4yYrVP3.mjs +428 -0
- package/fesm2022/magmonium-one-group-D4yYrVP3.mjs.map +1 -0
- package/fesm2022/magmonium-one-header-DmN8M1a3.mjs +380 -0
- package/fesm2022/magmonium-one-header-DmN8M1a3.mjs.map +1 -0
- package/fesm2022/magmonium-one-icon-Ep14YeRY.mjs +833 -0
- package/fesm2022/magmonium-one-icon-Ep14YeRY.mjs.map +1 -0
- package/fesm2022/magmonium-one-image-editor-BX4eNd4u.mjs +438 -0
- package/fesm2022/magmonium-one-image-editor-BX4eNd4u.mjs.map +1 -0
- package/fesm2022/magmonium-one-image-frame-BTSaUSAi.mjs +116 -0
- package/fesm2022/magmonium-one-image-frame-BTSaUSAi.mjs.map +1 -0
- package/fesm2022/magmonium-one-input-DqHPorA1.mjs +39 -0
- package/fesm2022/magmonium-one-input-DqHPorA1.mjs.map +1 -0
- package/fesm2022/magmonium-one-label-D2gjTbKX.mjs +574 -0
- package/fesm2022/magmonium-one-label-D2gjTbKX.mjs.map +1 -0
- package/fesm2022/magmonium-one-m-ref-Bq0gQbKE.mjs +147 -0
- package/fesm2022/magmonium-one-m-ref-Bq0gQbKE.mjs.map +1 -0
- package/fesm2022/magmonium-one-menu-BM9f2knw.mjs +887 -0
- package/fesm2022/magmonium-one-menu-BM9f2knw.mjs.map +1 -0
- package/fesm2022/magmonium-one-multi-range-a3ZVTy7i.mjs +458 -0
- package/fesm2022/magmonium-one-multi-range-a3ZVTy7i.mjs.map +1 -0
- package/fesm2022/magmonium-one-options-asset-CDEXIif6.mjs +97 -0
- package/fesm2022/magmonium-one-options-asset-CDEXIif6.mjs.map +1 -0
- package/fesm2022/magmonium-one-otp-DO4oTeSK.mjs +261 -0
- package/fesm2022/magmonium-one-otp-DO4oTeSK.mjs.map +1 -0
- package/fesm2022/magmonium-one-password-D3oTo41c.mjs +217 -0
- package/fesm2022/magmonium-one-password-D3oTo41c.mjs.map +1 -0
- package/fesm2022/magmonium-one-radio-CkaWESS3.mjs +111 -0
- package/fesm2022/magmonium-one-radio-CkaWESS3.mjs.map +1 -0
- package/fesm2022/magmonium-one-range-D_WEKrli.mjs +216 -0
- package/fesm2022/magmonium-one-range-D_WEKrli.mjs.map +1 -0
- package/fesm2022/magmonium-one-rolldown-runtime-DpJrqANO.mjs +11 -0
- package/fesm2022/magmonium-one-selector-BowevmOd.mjs +1065 -0
- package/fesm2022/magmonium-one-selector-BowevmOd.mjs.map +1 -0
- package/fesm2022/magmonium-one-size-converter-BfT0Aw0g.mjs +1060 -0
- package/fesm2022/magmonium-one-size-converter-BfT0Aw0g.mjs.map +1 -0
- package/fesm2022/magmonium-one-text-B4xKn-EQ.mjs +406 -0
- package/fesm2022/magmonium-one-text-B4xKn-EQ.mjs.map +1 -0
- package/fesm2022/magmonium-one-text-output-CfVeqYM7.mjs +296 -0
- package/fesm2022/magmonium-one-text-output-CfVeqYM7.mjs.map +1 -0
- package/fesm2022/magmonium-one-theme-Xgsx8bJs.mjs +1 -0
- package/fesm2022/magmonium-one-toggle-D2XEbN-b.mjs +231 -0
- package/fesm2022/magmonium-one-toggle-D2XEbN-b.mjs.map +1 -0
- package/fesm2022/magmonium-one.mjs +49250 -2
- package/fesm2022/magmonium-one.mjs.map +1 -1
- package/package.json +8 -8
- package/types/magmonium-one.d.ts +13909 -9533
- package/assets/menu/user_header.json +0 -27
- package/fesm2022/magmonium-one-image-editor-QPOy7XQt.mjs +0 -290
- package/fesm2022/magmonium-one-image-editor-QPOy7XQt.mjs.map +0 -1
- package/fesm2022/magmonium-one-magmonium-one-BTi-rHpF.mjs +0 -41583
- package/fesm2022/magmonium-one-magmonium-one-BTi-rHpF.mjs.map +0 -1
- package/fesm2022/magmonium-one-otp-BcNCgGpz.mjs +0 -201
- package/fesm2022/magmonium-one-otp-BcNCgGpz.mjs.map +0 -1
- package/fesm2022/magmonium-one-password-BDcSzmV_.mjs +0 -105
- package/fesm2022/magmonium-one-password-BDcSzmV_.mjs.map +0 -1
- package/fesm2022/magmonium-one-toggle-bLWTwEHE.mjs +0 -62
- package/fesm2022/magmonium-one-toggle-bLWTwEHE.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"magmonium-one-icon-Ep14YeRY.mjs","names":["catchError"],"sources":["../../../../libs/one/src/lib/shared/ui/icon/type/magmonium.ts","../../../../libs/one/src/lib/shared/ui/icon/model/shared-icons.ts","../../../../libs/one/src/lib/shared/ui/icon/lib/lib-icon.ts","../../../../libs/one/src/lib/shared/config/size-context.token.ts","../../../../libs/one/src/lib/shared/ui/icon/ui/icon.ts","../../../../libs/one/src/lib/shared/ui/icon/lib/custom-icon.ts","../../../../libs/one/src/lib/shared/ui/icon/ui/custom-icon.ts","../../../../libs/one/src/lib/shared/ui/icon/ui/custom-icon-edit.ts"],"sourcesContent":["export const MAGMONIUM = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"-15 -15 120 165\">\n <g>\n <path fill=\"#color\" style=\"opacity: 0.7;\" d=\"M5 5 L5 65 L85 65 Z\" />\n <path fill=\"#color\" style=\"opacity: 0.3;\" d=\"M85 5 L5 65 L85 65 Z\" />\n </g>\n <g>\n <path fill=\"#color\" style=\"opacity: 0.3;\" d=\"M5 70 L85 70 L5 130 Z\" />\n <path fill=\"#color\" style=\"opacity: 0.7;\" d=\"M85 70 L5 70 L85 130 Z\" />\n </g>\n </svg>\n `;\n","// AUTO-GENERATED by assets:compile — do not edit manually\nexport const SHARED_ICONS = [\n 'accordion_chevron',\n 'account_balance_wallet',\n 'activity',\n 'add-image',\n 'adjust',\n 'alert-circle',\n 'analytics',\n 'apps',\n 'arrow-down',\n 'arrow-left',\n 'arrow-left-right',\n 'arrow-right',\n 'arrow-up',\n 'arrow_back',\n 'bell',\n 'bold',\n 'books',\n 'calendar',\n 'calendar-open',\n 'camera',\n 'chat',\n 'check',\n 'chevron-down',\n 'chevron-left',\n 'chevron-right',\n 'chevron-up',\n 'chevron_left',\n 'chevrons-down',\n 'chevrons-left',\n 'chevrons-right',\n 'chevrons-up',\n 'clipboard',\n 'code',\n 'coins',\n 'color-picker',\n 'copy',\n 'copyright',\n 'corner-left-up',\n 'currency_exchange',\n 'dot',\n 'download',\n 'ellipsis-vertical',\n 'exclamation-triangle',\n 'eye',\n 'eye_close',\n 'file-code',\n 'file-data',\n 'file-image',\n 'file-markup',\n 'file-style',\n 'file-text',\n 'fixed',\n 'flower',\n 'folder',\n 'games',\n 'gear',\n 'heading',\n 'heart',\n 'heart_filled',\n 'help',\n 'hero',\n 'info',\n 'install',\n 'instrument',\n 'investments',\n 'italic',\n 'language',\n 'layers',\n 'layouts',\n 'link',\n 'link-broken',\n 'list',\n 'loading',\n 'loading-bars',\n 'loading-dots',\n 'loading-logo',\n 'loading-pulse',\n 'location',\n 'lock',\n 'logo',\n 'magazines',\n 'mail',\n 'menu',\n 'mic',\n 'minus',\n 'monitor',\n 'more',\n 'notification',\n 'notification_off',\n 'open',\n 'pause',\n 'pencil',\n 'play',\n 'plus',\n 'posts',\n 'quote',\n 'redo',\n 'reload',\n 'reset',\n 'save',\n 'scissors',\n 'search',\n 'section',\n 'security',\n 'settings',\n 'shield',\n 'smartphone',\n 'sort-alpha-asc',\n 'sort-alpha-desc',\n 'sparkles',\n 'square',\n 'star',\n 'store',\n 'strategies',\n 'strikethrough',\n 'sun',\n 'table',\n 'tablet',\n 'target',\n 'theme',\n 'trash',\n 'trash-2',\n 'trending-down',\n 'trending-up',\n 'undo',\n 'user',\n 'users',\n 'wallet_manager',\n 'wallet_owner',\n 'wallet_shareholder',\n 'wand',\n 'wifi',\n 'x',\n 'zap',\n];\n","import { Icon, MAGMONIUM } from '../type/icon';\nimport { ElementRef } from '@angular/core';\nimport { SHARED_ICONS } from '../model/shared-icons';\nexport { SHARED_ICONS };\n\nexport function isOneIcon(element: ElementRef<HTMLElement>, config?: Icon) {\n if (typeof config?.one !== 'undefined') {\n return !!config.one;\n }\n\n // cast SHARED_ICONS to a generic string array for runtime includes check\n if (config?.name && (SHARED_ICONS as readonly string[]).includes(config.name)) {\n return true;\n }\n\n const tagName = element.nativeElement.tagName;\n return tagName === 'M-ONE-ICON';\n}\n\nfunction textToSvg(str = MAGMONIUM, color: string): string {\n if (typeof str !== 'string') return str as unknown as string;\n return str.replace(/#color/g, color).replace(/currentColor/g, color);\n}\n\nexport function svgElementFromString(str: string, color: string): SVGElement {\n const parser = new DOMParser();\n const doc = parser.parseFromString(textToSvg(str, color), 'image/svg+xml');\n const svg = doc.documentElement;\n\n if (svg && svg.nodeName === 'svg') {\n return svg as unknown as SVGElement;\n }\n\n const errorSvg = parser.parseFromString(textToSvg(MAGMONIUM, color), 'image/svg+xml');\n return errorSvg.documentElement as unknown as SVGElement;\n}\n","import { inject, InjectionToken, Provider, Signal, Type } from '@angular/core';\nimport { Size } from '../enum/scaling/size';\n\n/**\n * The Size in force for a subtree. Provided by the only components that may\n * declare one — Section, ButtonGroup, and the markup-composed SectionButtonGroup\n * (the last two are one seat, ADR 0008) — and injected `{ optional: true }` by\n * each of them (so a declarer falls back to the nearest one above it) and by\n * the few primitives whose size is a box dimension rather than a font size.\n *\n * Most components need nothing: a declarer also sets `font-size` on its own\n * host, so anything measured in `em` follows through ordinary CSS inheritance.\n * The token exists for what inheritance cannot do — a ButtonGroup or\n * SectionButtonGroup overriding its Section, and Icon sizing a square glyph.\n * See ADR 0008.\n */\nexport interface SizeContext {\n readonly size: Signal<Size>;\n}\n\nexport const SIZE_CONTEXT = new InjectionToken<SizeContext>('SIZE_CONTEXT');\n\nexport interface SizeDeclarer {\n readonly resolvedSize: Signal<Size>;\n}\n\n/**\n * Publishes a declarer's resolved Size to its subtree. Goes in `providers` and\n * never `viewProviders`: a Section's Cols and a ButtonGroup's Buttons are\n * projected content, which only `providers` reaches.\n */\nexport function provideSizeContext(\n declarer: Type<SizeDeclarer>\n): Provider {\n return {\n provide: SIZE_CONTEXT,\n useFactory: (): SizeContext => ({ size: inject(declarer).resolvedSize }),\n };\n}\n\n/**\n * The Size in force above a declarer. `skipSelf` because a declarer provides\n * SIZE_CONTEXT itself, and injecting its own provider would be circular — what\n * it wants is the nearest declarer *above* it, so that a Section with no Size\n * of its own takes its parent Section's and a ButtonGroup takes its Section's.\n */\nexport function injectParentSize(): SizeContext | null {\n return inject(SIZE_CONTEXT, { optional: true, skipSelf: true });\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n input,\n model,\n untracked,\n} from '@angular/core';\nimport { Icon, MAGMONIUM } from '../type/icon';\nimport { isOneIcon, svgElementFromString } from '../lib/lib-icon';\nimport { SIZE_CONTEXT } from '../../../config/size-context.token';\nimport { resolveIconSize } from '../../../functions/size/size-converter';\nimport { AssetStore } from '../../../stores/asset.store';\nimport { HttpService } from '../../../services/http';\nimport { rxResource } from '@angular/core/rxjs-interop';\nimport { catchError } from 'rxjs/operators';\nimport { of } from 'rxjs';\nimport { applyColorsToElement, ThemeStore } from '../../../lib/theme';\nimport { ICON_SOURCE } from '../../../config/tokens';\n\n@Component({\n selector: 'm-icon, m-one-icon',\n template: `<ng-content></ng-content>`,\n styleUrls: ['./icon.sass'],\n host: {\n '[class.has-color]': 'resolvedConfig()?.color',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IconComponent {\n // Kept for the callers that hand an Icon a whole object rather than write it\n // out — a Button naming its own glyph, a SectionFormItem passing a Field's.\n config = model<Icon | undefined>(undefined);\n\n // `name` is the SVG's filename in `mag_assets/icons/`, not a config asset:\n // an Icon resolves artwork, so it is the one primitive whose name points at\n // a picture rather than at a description of itself (ADR 0010). `size` is a\n // free length (number → em), not a Size — Size stays on Section / ButtonGroup\n // (ADR 0008); set here only to overwrite the box SIZE_CONTEXT would give.\n readonly name = input<string | undefined>();\n readonly color = input<string | undefined>();\n readonly size = input<number | string | undefined>();\n readonly one = input<boolean | undefined>();\n readonly baseUrl = input<string | undefined>();\n readonly remote = input<string | undefined>();\n\n // Attributes win over the bound object, and an omitted one leaves the\n // object's key standing rather than blanking it.\n protected readonly resolvedConfig = computed<Icon | undefined>(() => {\n const config = this.config();\n const overrides: Icon = {};\n const name = this.name();\n if (name !== undefined) overrides.name = name;\n const color = this.color();\n if (color !== undefined) overrides.color = color;\n const one = this.one();\n if (one !== undefined) overrides.one = one;\n const baseUrl = this.baseUrl();\n if (baseUrl !== undefined) overrides.baseUrl = baseUrl;\n const remote = this.remote();\n if (remote !== undefined) overrides.remote = remote;\n if (!config && !Object.keys(overrides).length) return undefined;\n return { ...config, ...overrides };\n });\n\n private readonly elementRef = inject(ElementRef);\n // Icon sizes a square glyph through width/height, which no amount of\n // inherited font-size can set — so it is one of the few primitives that\n // reads the Size in force directly (ADR 0008).\n private readonly sizeContext = inject(SIZE_CONTEXT, { optional: true });\n private readonly assetStore = inject(AssetStore);\n private readonly http = inject(HttpService);\n private currentSvgElement: SVGElement | null = null;\n private readonly themeStore = inject(ThemeStore);\n private readonly isOneIcon = computed(() =>\n isOneIcon(this.elementRef, this.resolvedConfig())\n );\n private readonly remoteBaseUrl = computed<string | undefined>(() => {\n const remoteId = this.resolvedConfig()?.remote;\n return remoteId ? this.http.buildWcAssetBaseUrl(remoteId) : undefined;\n });\n\n // Icons supplied by name from memory (e.g. a visual editor's unsaved glyphs).\n // Read as a signal rather than through the resource below so that changing the\n // markup under an unchanged name repaints every icon already using it.\n private readonly iconSource = inject(ICON_SOURCE, { optional: true });\n private readonly inlineSvg = computed(() => {\n const name = this.resolvedConfig()?.name;\n return name ? this.iconSource?.()?.[name] : undefined;\n });\n\n private readonly text = rxResource({\n params: computed(\n () => ({\n name: this.resolvedConfig()?.name,\n isOneIcon: untracked(() => this.isOneIcon()),\n remoteBaseUrl: this.remoteBaseUrl(),\n }),\n { equal: (a, b) => a?.name === b?.name && a?.remoteBaseUrl === b?.remoteBaseUrl }\n ),\n stream: ({ params: { name, isOneIcon, remoteBaseUrl } }) => {\n if (!name) return untracked(() =>\n this.assetStore.resolve<string>('icons', 'logo', true).pipe(catchError(() => of(MAGMONIUM)))\n );\n // Skips the request entirely for an in-memory glyph — there is no asset\n // behind it, so fetching only ever produces a 404 and the fallback logo.\n const inline = untracked(() => this.inlineSvg());\n if (inline) return of(inline);\n return untracked(() =>\n this.assetStore\n .resolve<string>('icons', name, isOneIcon, remoteBaseUrl)\n .pipe(catchError(() => of(MAGMONIUM)))\n );\n },\n });\n\n private readonly pallet = computed(() =>\n applyColorsToElement(\n this.resolvedConfig()?.color ?? this.themeStore.color()?.mm ?? '#fff',\n this.themeStore.color(),\n this.elementRef.nativeElement\n )\n );\n\n constructor() {\n effect(this.updateElement);\n effect(this.applySize);\n }\n\n private readonly applySize = (): void => {\n this.elementRef.nativeElement.style.setProperty(\n '--m-icon-size',\n resolveIconSize(this.size(), this.sizeContext?.size())\n );\n };\n\n private readonly updateElement = (): void => {\n const element = this.elementRef.nativeElement;\n if (this.currentSvgElement) {\n this.currentSvgElement.remove();\n this.currentSvgElement = null;\n }\n const svg = svgElementFromString(\n this.inlineSvg() ?? (this.text.value() as string),\n this.pallet()?.color ?? this.themeStore.color()?.mm ?? '#fff'\n );\n if (!svg) return;\n this.currentSvgElement = svg;\n svg.removeAttribute('width');\n svg.removeAttribute('height');\n svg.style.width = 'var(--m-icon-size, 1em)';\n svg.style.height = 'var(--m-icon-size, 1em)';\n svg.style.flexShrink = '0';\n svg.removeAttribute('stroke-width');\n svg.style.setProperty('stroke-width', '2px');\n svg.querySelectorAll('*').forEach((el) => {\n el.removeAttribute('stroke-width');\n if (el instanceof HTMLElement || el instanceof SVGElement) {\n el.style.removeProperty('stroke-width');\n }\n });\n element.prepend(svg);\n };\n}\n","import { CustomIcon } from '../type/icon';\nimport { ThemeColor } from '@magmonium/cli';\n\nfunction genColor(colors: ThemeColor, color: string) {\n return colors[color as keyof ThemeColor] ?? color;\n}\n\nexport function isLargeImage(svg: CustomIcon): boolean {\n const width = svg.width || 0;\n const height = svg.height || 0;\n const area = width * height;\n return area > 2000000 || width > 2000 || height > 2000;\n}\n\nexport function genViewPort(svg: CustomIcon | undefined) {\n if (!svg) return '0 0 0 0';\n const width = svg.width || 0;\n const height = svg.height || 0;\n return `0 0 ${width} ${height}`;\n}\n\nexport function computedFillColor(\n svg: CustomIcon,\n colors?: ThemeColor,\n fillColor?: string\n) {\n const { lineColor, backgroundColor, invertSvg } = svg.edit;\n if (!colors) {\n return fillColor ?? backgroundColor;\n }\n return genColor(\n colors,\n invertSvg ? lineColor : (fillColor ?? backgroundColor)\n );\n}\n\nexport function computeLineColor(\n svg: CustomIcon,\n colors?: ThemeColor,\n strokeColor?: string\n) {\n const { lineColor, backgroundColor, invertSvg } = svg.edit;\n if (!colors) {\n return strokeColor ?? lineColor;\n }\n return genColor(\n colors,\n invertSvg ? backgroundColor : (strokeColor ?? lineColor)\n );\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n input,\n} from '@angular/core';\nimport { CustomIcon } from '../type/icon';\nimport {\n computedFillColor,\n computeLineColor,\n genViewPort,\n isLargeImage,\n} from '../lib/custom-icon';\nimport { ThemeStore } from '../../../lib/theme';\n\n@Component({\n selector: 'm-custom-icon',\n template: `\n <!-- eslint-disable local/no-raw-svg -->\n @if (svg(); as svg) {\n <svg\n xmlns=\"http:/www.w3.org/2000/svg\"\n [attr.viewBox]=\"viewBox()\"\n [attr.width]=\"svg.width\"\n [attr.height]=\"svg.height\"\n preserveAspectRatio=\"xMidYMid meet\"\n >\n @if (_fillColor(); as fillColor) {\n <rect\n [attr.width]=\"svg.width\"\n [attr.height]=\"svg.height\"\n [attr.fill]=\"fillColor\"\n />\n }\n @if (_strokeColor(); as stroke) {\n @for (path of svg?.paths; track $index) {\n <path\n class=\"svg-path\"\n [attr.d]=\"path\"\n [attr.fill]=\"'none'\"\n [attr.stroke]=\"stroke\"\n [attr.stroke-linecap]=\"svg?.edit?.linecap\"\n [attr.stroke-linejoin]=\"svg?.edit?.linejoin\"\n [attr.stroke-width]=\"svg.edit.strokeWidth\"\n [attr.vector-effect]=\"vectorEffect()\"\n />\n }\n }\n </svg>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CustomIconClass {\n svg = input.required<CustomIcon>();\n strokeColor = input<string | undefined>(undefined);\n fillColor = input<string | undefined>(undefined);\n strokeWidth = input<number | undefined>(undefined);\n\n protected readonly viewBox = computed(() => genViewPort(this.svg()));\n protected readonly vectorEffect = computed(() => {\n return isLargeImage(this.svg()) ? 'non-scaling-stroke' : null;\n });\n private readonly themeStore = inject(ThemeStore);\n protected readonly _fillColor = computed(() =>\n computedFillColor(this.svg(), this.themeStore.color(), this.fillColor())\n );\n protected readonly _strokeColor = computed(() => {\n return computeLineColor(\n this.svg(),\n this.themeStore.color(),\n this.strokeColor()\n );\n });\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n HostListener,\n computed,\n inject,\n input,\n model,\n} from '@angular/core';\nimport { CustomIcon } from '../type/icon';\nimport {\n computedFillColor,\n computeLineColor,\n genViewPort,\n isLargeImage,\n} from '../lib/custom-icon';\nimport { ThemeStore } from '../../../lib/theme';\n\n@Component({\n selector: 'm-custom-icon-edit',\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <!-- eslint-disable local/no-raw-svg -->\n @if (svg(); as svg) {\n <svg\n xmlns=\"http:/www.w3.org/2000/svg\"\n [attr.viewBox]=\"_viewBox()\"\n [attr.width]=\"svg.width\"\n [attr.height]=\"svg.height\"\n preserveAspectRatio=\"xMidYMid meet\"\n >\n @if (_fillColor(); as fillColor) {\n <rect\n [attr.width]=\"svg.width\"\n [attr.height]=\"svg.height\"\n [attr.fill]=\"fillColor\"\n />\n }\n @for (path of svg.paths; track $index) {\n <path\n class=\"svg-path\"\n (click)=\"_onPathClick($index)\"\n [class.interactive]=\"svg.edit.canInteract\"\n [class.selected]=\"_selectedIndex.includes($index)\"\n [attr.d]=\"path\"\n [attr.fill]=\"'none'\"\n [attr.stroke]=\"_strokeColor()\"\n [attr.stroke-linecap]=\"svg.edit.linecap\"\n [attr.stroke-linejoin]=\"svg.edit.linejoin\"\n [attr.stroke-width]=\"svg.edit.strokeWidth\"\n [attr.vector-effect]=\"_vectorEffect()\"\n />\n }\n </svg>\n }\n `,\n styleUrl: './custom-icon-edit.sass',\n})\nexport class CustomIconEditComponent {\n svg = model<CustomIcon | undefined>(undefined);\n strokeColor = input<string | undefined>(undefined);\n fillColor = input<string | undefined>(undefined);\n\n private readonly themeStore = inject(ThemeStore);\n\n @HostListener('window:keydown', ['$event'])\n onKeydown(event: KeyboardEvent) {\n if (!this.svg()?.edit?.canInteract || this._selectedIndex.length === 0) {\n return;\n }\n if (event.key === ' ') {\n event.preventDefault();\n this.deleteSelectedPaths();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n this._selectedIndex = [];\n }\n }\n\n protected readonly _viewBox = computed(() => genViewPort(this.svg()));\n\n protected readonly _vectorEffect = computed(() => {\n const svg = this.svg();\n return svg && isLargeImage(svg) ? 'non-scaling-stroke' : null;\n });\n\n protected readonly _fillColor = computed(() => {\n const svg = this.svg();\n if (!svg) return undefined;\n return computedFillColor(svg, this.themeStore.color(), this.fillColor());\n });\n\n protected readonly _strokeColor = computed(() => {\n const svg = this.svg();\n if (!svg) return undefined;\n return computeLineColor(svg, this.themeStore.color(), this.strokeColor());\n });\n\n protected readonly _onPathClick = (index: number) => {\n if (!this.svg()?.edit?.canInteract) return;\n this._selectedIndex = Array.from(new Set([...this._selectedIndex, index]));\n };\n\n readonly deleteSelectedPaths = () => {\n this.svg.update((prev) => {\n if (!prev) return prev;\n return {\n ...prev,\n paths: prev.paths.filter((_, i) => !this._selectedIndex.includes(i)),\n };\n });\n this._selectedIndex = [];\n };\n\n protected _selectedIndex: number[] = [];\n}\n"],"mappings":";;;;;;;;AAAA,MAAa,YAAY;;;;;;;;;;;;ACCzB,MAAa,eAAe;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;ACnID,SAAgB,UAAU,SAAkC,QAAa;CACvE,IAAI,OAAO,QAAQ,QAAQ,aACzB,OAAO,CAAC,CAAC,OAAO;CAIlB,IAAI,QAAQ,QAAS,aAAmC,SAAS,OAAO,IAAI,GAC1E,OAAO;CAIT,OADgB,QAAQ,cAAc,YACnB;AACrB;AAEA,SAAS,UAAU,MAAM,WAAW,OAAa;CAC/C,IAAI,OAAO,QAAQ,UAAU,OAAO;CACpC,OAAO,IAAI,QAAQ,WAAW,KAAK,CAAC,CAAC,QAAQ,iBAAiB,KAAK;AACrE;AAEA,SAAgB,qBAAqB,KAAa,OAAa;CAC7D,MAAM,SAAS,IAAI,UAAS;CAE5B,MAAM,MADM,OAAO,gBAAgB,UAAU,KAAK,KAAK,GAAG,eAC9C,CAAA,CAAI;CAEhB,IAAI,OAAO,IAAI,aAAa,OAC1B,OAAO;CAIT,OADiB,OAAO,gBAAgB,UAAU,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"}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { t as ButtonComponent } from "./magmonium-one-button-Bwzaxxn3.mjs";
|
|
2
|
+
import { t as HeaderComponent } from "./magmonium-one-header-DmN8M1a3.mjs";
|
|
3
|
+
import { t as InputType } from "./magmonium-one-input-DqHPorA1.mjs";
|
|
4
|
+
import { t as RangeInputComponent } from "./magmonium-one-range-D_WEKrli.mjs";
|
|
5
|
+
import { a as initialFraming, c as parseRatio, i as encodeFramed, l as renameForType, n as cropRect, r as drawFramed, s as outputSize, t as clampFraming } from "./magmonium-one-image-frame-BTSaUSAi.mjs";
|
|
6
|
+
import { MODAL_REF } from "./magmonium-one.mjs";
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, computed, effect, inject, input, signal, viewChild } from "@angular/core";
|
|
9
|
+
const WHEEL_ZOOM_RATE = .0015;
|
|
10
|
+
const distance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
|
|
11
|
+
var ImageEditorComponent = class ImageEditorComponent {
|
|
12
|
+
file = input.required(...ngDevMode ? [{ debugName: "file" }] : /* istanbul ignore next */ []);
|
|
13
|
+
ratio = input.required(...ngDevMode ? [{ debugName: "ratio" }] : /* istanbul ignore next */ []);
|
|
14
|
+
width = input.required(...ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []);
|
|
15
|
+
#modalRef = inject(MODAL_REF);
|
|
16
|
+
stage = viewChild("stage", ...ngDevMode ? [{ debugName: "stage" }] : /* istanbul ignore next */ []);
|
|
17
|
+
source = signal(void 0, ...ngDevMode ? [{ debugName: "source" }] : /* istanbul ignore next */ []);
|
|
18
|
+
sourceUrl = signal(void 0, ...ngDevMode ? [{ debugName: "sourceUrl" }] : /* istanbul ignore next */ []);
|
|
19
|
+
isApplying = signal(false, ...ngDevMode ? [{ debugName: "isApplying" }] : /* istanbul ignore next */ []);
|
|
20
|
+
framing = signal({
|
|
21
|
+
cx: 0,
|
|
22
|
+
cy: 0,
|
|
23
|
+
zoom: 1
|
|
24
|
+
}, ...ngDevMode ? [{ debugName: "framing" }] : /* istanbul ignore next */ []);
|
|
25
|
+
stageWidth = signal(0, ...ngDevMode ? [{ debugName: "stageWidth" }] : /* istanbul ignore next */ []);
|
|
26
|
+
pointers = /* @__PURE__ */ new Map();
|
|
27
|
+
isPanning = signal(false, ...ngDevMode ? [{ debugName: "isPanning" }] : /* istanbul ignore next */ []);
|
|
28
|
+
#settled = false;
|
|
29
|
+
zoomConfig = computed(() => ({
|
|
30
|
+
name: "zoom",
|
|
31
|
+
label: "zoom",
|
|
32
|
+
type: InputType.RANGE,
|
|
33
|
+
min: 100,
|
|
34
|
+
max: 400
|
|
35
|
+
}), ...ngDevMode ? [{ debugName: "zoomConfig" }] : /* istanbul ignore next */ []);
|
|
36
|
+
aspect = computed(() => parseRatio(this.ratio()) ?? 1, ...ngDevMode ? [{ debugName: "aspect" }] : /* istanbul ignore next */ []);
|
|
37
|
+
output = computed(() => outputSize(this.ratio(), this.width()), ...ngDevMode ? [{ debugName: "output" }] : /* istanbul ignore next */ []);
|
|
38
|
+
sizeParams = computed(() => {
|
|
39
|
+
const size = this.output();
|
|
40
|
+
return {
|
|
41
|
+
width: String(size?.width ?? ""),
|
|
42
|
+
height: String(size?.height ?? "")
|
|
43
|
+
};
|
|
44
|
+
}, ...ngDevMode ? [{ debugName: "sizeParams" }] : /* istanbul ignore next */ []);
|
|
45
|
+
imageSize = computed(() => {
|
|
46
|
+
const bitmap = this.source();
|
|
47
|
+
return bitmap ? {
|
|
48
|
+
width: bitmap.width,
|
|
49
|
+
height: bitmap.height
|
|
50
|
+
} : void 0;
|
|
51
|
+
}, ...ngDevMode ? [{ debugName: "imageSize" }] : /* istanbul ignore next */ []);
|
|
52
|
+
zoomPercent = computed(() => String(Math.round(this.framing().zoom * 100)), ...ngDevMode ? [{ debugName: "zoomPercent" }] : /* istanbul ignore next */ []);
|
|
53
|
+
imageBox = computed(() => {
|
|
54
|
+
const image = this.imageSize();
|
|
55
|
+
const stage = this.stageWidth();
|
|
56
|
+
if (!image || !stage) return {
|
|
57
|
+
width: 0,
|
|
58
|
+
height: 0,
|
|
59
|
+
transform: "none"
|
|
60
|
+
};
|
|
61
|
+
const rect = cropRect(image, this.aspect(), this.framing());
|
|
62
|
+
const scale = stage / rect.width;
|
|
63
|
+
return {
|
|
64
|
+
width: image.width * scale,
|
|
65
|
+
height: image.height * scale,
|
|
66
|
+
transform: `translate(${-rect.x * scale}px, ${-rect.y * scale}px)`
|
|
67
|
+
};
|
|
68
|
+
}, ...ngDevMode ? [{ debugName: "imageBox" }] : /* istanbul ignore next */ []);
|
|
69
|
+
load = effect((onCleanup) => {
|
|
70
|
+
const file = this.file();
|
|
71
|
+
let cancelled = false;
|
|
72
|
+
const url = URL.createObjectURL(file);
|
|
73
|
+
this.sourceUrl.set(url);
|
|
74
|
+
createImageBitmap(file, { imageOrientation: "from-image" }).then((bitmap) => {
|
|
75
|
+
if (cancelled) {
|
|
76
|
+
bitmap.close();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.framing.set(initialFraming({
|
|
80
|
+
width: bitmap.width,
|
|
81
|
+
height: bitmap.height
|
|
82
|
+
}));
|
|
83
|
+
this.source.set(bitmap);
|
|
84
|
+
}).catch(() => {
|
|
85
|
+
if (!cancelled) this.settle({ unreadable: true });
|
|
86
|
+
});
|
|
87
|
+
onCleanup(() => {
|
|
88
|
+
cancelled = true;
|
|
89
|
+
URL.revokeObjectURL(url);
|
|
90
|
+
this.source()?.close();
|
|
91
|
+
});
|
|
92
|
+
}, ...ngDevMode ? [{ debugName: "load" }] : /* istanbul ignore next */ []);
|
|
93
|
+
measure = effect((onCleanup) => {
|
|
94
|
+
const stage = this.stage()?.nativeElement;
|
|
95
|
+
if (!stage) return;
|
|
96
|
+
this.stageWidth.set(stage.clientWidth);
|
|
97
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
98
|
+
const observer = new ResizeObserver(([entry]) => this.stageWidth.set(entry.contentRect.width));
|
|
99
|
+
observer.observe(stage);
|
|
100
|
+
onCleanup(() => observer.disconnect());
|
|
101
|
+
}, ...ngDevMode ? [{ debugName: "measure" }] : /* istanbul ignore next */ []);
|
|
102
|
+
reportDismissal = inject(DestroyRef).onDestroy(() => this.settle(void 0));
|
|
103
|
+
onPointerDown = (event) => {
|
|
104
|
+
if (!this.source()) return;
|
|
105
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
106
|
+
this.pointers.set(event.pointerId, {
|
|
107
|
+
x: event.clientX,
|
|
108
|
+
y: event.clientY
|
|
109
|
+
});
|
|
110
|
+
this.isPanning.set(true);
|
|
111
|
+
};
|
|
112
|
+
onPointerMove = (event) => {
|
|
113
|
+
const previous = this.pointers.get(event.pointerId);
|
|
114
|
+
if (!previous) return;
|
|
115
|
+
const next = {
|
|
116
|
+
x: event.clientX,
|
|
117
|
+
y: event.clientY
|
|
118
|
+
};
|
|
119
|
+
const others = [...this.pointers.entries()].filter(([id]) => id !== event.pointerId);
|
|
120
|
+
if (others.length === 1) {
|
|
121
|
+
const anchor = others[0][1];
|
|
122
|
+
const before = distance(previous, anchor);
|
|
123
|
+
if (before > 0) this.zoomBy(distance(next, anchor) / before);
|
|
124
|
+
} else if (others.length === 0) this.panBy(next.x - previous.x, next.y - previous.y);
|
|
125
|
+
this.pointers.set(event.pointerId, next);
|
|
126
|
+
};
|
|
127
|
+
onPointerUp = (event) => {
|
|
128
|
+
this.pointers.delete(event.pointerId);
|
|
129
|
+
this.isPanning.set(this.pointers.size > 0);
|
|
130
|
+
};
|
|
131
|
+
onWheel = (event) => {
|
|
132
|
+
if (!this.source()) return;
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
this.zoomBy(Math.exp(-event.deltaY * WHEEL_ZOOM_RATE));
|
|
135
|
+
};
|
|
136
|
+
onZoom = (percent) => {
|
|
137
|
+
const zoom = Number(percent) / 100;
|
|
138
|
+
if (!Number.isFinite(zoom)) return;
|
|
139
|
+
this.updateFraming((framing) => ({
|
|
140
|
+
...framing,
|
|
141
|
+
zoom
|
|
142
|
+
}));
|
|
143
|
+
};
|
|
144
|
+
apply = async () => {
|
|
145
|
+
const bitmap = this.source();
|
|
146
|
+
const image = this.imageSize();
|
|
147
|
+
const output = this.output();
|
|
148
|
+
if (!bitmap || !image || !output || this.isApplying()) return;
|
|
149
|
+
this.isApplying.set(true);
|
|
150
|
+
try {
|
|
151
|
+
const rect = cropRect(image, this.aspect(), this.framing());
|
|
152
|
+
const blob = await encodeFramed(drawFramed(bitmap, rect, output));
|
|
153
|
+
const original = this.file();
|
|
154
|
+
this.settle({ file: new File([blob], renameForType(original.name, blob.type), {
|
|
155
|
+
type: blob.type,
|
|
156
|
+
lastModified: Date.now()
|
|
157
|
+
}) });
|
|
158
|
+
} catch {
|
|
159
|
+
this.settle({ unreadable: true });
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
panBy = (dx, dy) => {
|
|
163
|
+
const image = this.imageSize();
|
|
164
|
+
const stage = this.stageWidth();
|
|
165
|
+
if (!image || !stage) return;
|
|
166
|
+
const perPixel = cropRect(image, this.aspect(), this.framing()).width / stage;
|
|
167
|
+
this.updateFraming((framing) => ({
|
|
168
|
+
...framing,
|
|
169
|
+
cx: framing.cx - dx * perPixel,
|
|
170
|
+
cy: framing.cy - dy * perPixel
|
|
171
|
+
}));
|
|
172
|
+
};
|
|
173
|
+
zoomBy = (factor) => {
|
|
174
|
+
this.updateFraming((framing) => ({
|
|
175
|
+
...framing,
|
|
176
|
+
zoom: framing.zoom * factor
|
|
177
|
+
}));
|
|
178
|
+
};
|
|
179
|
+
updateFraming = (change) => {
|
|
180
|
+
const image = this.imageSize();
|
|
181
|
+
if (!image) return;
|
|
182
|
+
this.framing.update((framing) => clampFraming(image, this.aspect(), change(framing)));
|
|
183
|
+
};
|
|
184
|
+
settle = (result) => {
|
|
185
|
+
if (this.#settled) return;
|
|
186
|
+
this.#settled = true;
|
|
187
|
+
this.#modalRef.close(result);
|
|
188
|
+
};
|
|
189
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
190
|
+
minVersion: "12.0.0",
|
|
191
|
+
version: "22.2.1",
|
|
192
|
+
ngImport: i0,
|
|
193
|
+
type: ImageEditorComponent,
|
|
194
|
+
deps: [],
|
|
195
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
196
|
+
});
|
|
197
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
198
|
+
minVersion: "17.0.0",
|
|
199
|
+
version: "22.2.1",
|
|
200
|
+
type: ImageEditorComponent,
|
|
201
|
+
isStandalone: true,
|
|
202
|
+
selector: "m-image-editor",
|
|
203
|
+
inputs: {
|
|
204
|
+
file: {
|
|
205
|
+
classPropertyName: "file",
|
|
206
|
+
publicName: "file",
|
|
207
|
+
isSignal: true,
|
|
208
|
+
isRequired: true,
|
|
209
|
+
transformFunction: null
|
|
210
|
+
},
|
|
211
|
+
ratio: {
|
|
212
|
+
classPropertyName: "ratio",
|
|
213
|
+
publicName: "ratio",
|
|
214
|
+
isSignal: true,
|
|
215
|
+
isRequired: true,
|
|
216
|
+
transformFunction: null
|
|
217
|
+
},
|
|
218
|
+
width: {
|
|
219
|
+
classPropertyName: "width",
|
|
220
|
+
publicName: "width",
|
|
221
|
+
isSignal: true,
|
|
222
|
+
isRequired: true,
|
|
223
|
+
transformFunction: null
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
viewQueries: [{
|
|
227
|
+
propertyName: "stage",
|
|
228
|
+
first: true,
|
|
229
|
+
predicate: ["stage"],
|
|
230
|
+
descendants: true,
|
|
231
|
+
isSignal: true
|
|
232
|
+
}],
|
|
233
|
+
ngImport: i0,
|
|
234
|
+
template: `
|
|
235
|
+
<div class="image-editor">
|
|
236
|
+
<m-header
|
|
237
|
+
[level]="3"
|
|
238
|
+
label="edit-image-size"
|
|
239
|
+
[params]="sizeParams()"
|
|
240
|
+
/>
|
|
241
|
+
<div
|
|
242
|
+
#stage
|
|
243
|
+
class="image-editor__stage"
|
|
244
|
+
[style.aspect-ratio]="aspect()"
|
|
245
|
+
[style.--m-image-editor-aspect]="aspect()"
|
|
246
|
+
[class.image-editor__stage--panning]="isPanning()"
|
|
247
|
+
(pointerdown)="onPointerDown($event)"
|
|
248
|
+
(pointermove)="onPointerMove($event)"
|
|
249
|
+
(pointerup)="onPointerUp($event)"
|
|
250
|
+
(pointercancel)="onPointerUp($event)"
|
|
251
|
+
(wheel)="onWheel($event)"
|
|
252
|
+
>
|
|
253
|
+
@if (sourceUrl(); as url) {
|
|
254
|
+
<img
|
|
255
|
+
class="image-editor__image"
|
|
256
|
+
[src]="url"
|
|
257
|
+
alt=""
|
|
258
|
+
draggable="false"
|
|
259
|
+
[style.width.px]="imageBox().width"
|
|
260
|
+
[style.height.px]="imageBox().height"
|
|
261
|
+
[style.transform]="imageBox().transform"
|
|
262
|
+
/>
|
|
263
|
+
}
|
|
264
|
+
</div>
|
|
265
|
+
<m-range
|
|
266
|
+
[config]="zoomConfig()"
|
|
267
|
+
[value]="zoomPercent()"
|
|
268
|
+
(valueChange)="onZoom($event)"
|
|
269
|
+
/>
|
|
270
|
+
<!-- asset: buttons/okay.yml. No cancel beside it: the Modal's own close
|
|
271
|
+
is the cancel, and it drops the image the same way. -->
|
|
272
|
+
<m-one-button
|
|
273
|
+
name="okay"
|
|
274
|
+
label="apply"
|
|
275
|
+
[disabled]="!source() || isApplying()"
|
|
276
|
+
(clicked)="apply()"
|
|
277
|
+
/>
|
|
278
|
+
</div>
|
|
279
|
+
`,
|
|
280
|
+
isInline: true,
|
|
281
|
+
styles: ["@keyframes toggle-pulse{0%,to{transform:scale(1)}50%{transform:scale(1.02)}}@keyframes thumb-slide{0%{transform:translate(0) scale(1)}50%{transform:translate(.3em) scale(.9)}to{transform:translate(.6em) scale(1)}}:host{display:block}.image-editor{display:flex;flex-direction:column;gap:1rem;width:100%;min-width:0;padding:1.5rem}.image-editor__stage{position:relative;width:100%;max-width:calc((90vh - 16rem) * var(--m-image-editor-aspect, 1));margin-inline:auto;overflow:hidden;border:1px solid var(--m-border);background:var(--m-background-lite);cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}.image-editor__stage--panning{cursor:grabbing}.image-editor__image{position:absolute;top:0;left:0;max-width:none;transform-origin:0 0;pointer-events:none}\n"],
|
|
282
|
+
dependencies: [
|
|
283
|
+
{
|
|
284
|
+
kind: "component",
|
|
285
|
+
type: ButtonComponent,
|
|
286
|
+
selector: "m-button,m-one-button",
|
|
287
|
+
inputs: [
|
|
288
|
+
"config",
|
|
289
|
+
"color",
|
|
290
|
+
"labelClass",
|
|
291
|
+
"fullWidth",
|
|
292
|
+
"nav",
|
|
293
|
+
"navParams",
|
|
294
|
+
"href",
|
|
295
|
+
"disabled",
|
|
296
|
+
"variant",
|
|
297
|
+
"name",
|
|
298
|
+
"baseUrl",
|
|
299
|
+
"one",
|
|
300
|
+
"remote",
|
|
301
|
+
"label",
|
|
302
|
+
"noTranslate",
|
|
303
|
+
"params",
|
|
304
|
+
"noNative",
|
|
305
|
+
"icon",
|
|
306
|
+
"isRightIcon",
|
|
307
|
+
"iconOnly",
|
|
308
|
+
"stacked",
|
|
309
|
+
"imgSrc",
|
|
310
|
+
"nonClickable",
|
|
311
|
+
"inverted",
|
|
312
|
+
"isSubmit",
|
|
313
|
+
"tabindex"
|
|
314
|
+
],
|
|
315
|
+
outputs: ["configChange", "clicked"]
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
kind: "component",
|
|
319
|
+
type: HeaderComponent,
|
|
320
|
+
selector: "m-header",
|
|
321
|
+
inputs: [
|
|
322
|
+
"level",
|
|
323
|
+
"label",
|
|
324
|
+
"icon",
|
|
325
|
+
"actionButton",
|
|
326
|
+
"actionLabel",
|
|
327
|
+
"noTranslate",
|
|
328
|
+
"params"
|
|
329
|
+
],
|
|
330
|
+
outputs: ["actionClick"]
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
kind: "component",
|
|
334
|
+
type: RangeInputComponent,
|
|
335
|
+
selector: "m-range"
|
|
336
|
+
}
|
|
337
|
+
],
|
|
338
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
342
|
+
minVersion: "12.0.0",
|
|
343
|
+
version: "22.2.1",
|
|
344
|
+
ngImport: i0,
|
|
345
|
+
type: ImageEditorComponent,
|
|
346
|
+
decorators: [{
|
|
347
|
+
type: Component,
|
|
348
|
+
args: [{
|
|
349
|
+
selector: "m-image-editor",
|
|
350
|
+
template: `
|
|
351
|
+
<div class="image-editor">
|
|
352
|
+
<m-header
|
|
353
|
+
[level]="3"
|
|
354
|
+
label="edit-image-size"
|
|
355
|
+
[params]="sizeParams()"
|
|
356
|
+
/>
|
|
357
|
+
<div
|
|
358
|
+
#stage
|
|
359
|
+
class="image-editor__stage"
|
|
360
|
+
[style.aspect-ratio]="aspect()"
|
|
361
|
+
[style.--m-image-editor-aspect]="aspect()"
|
|
362
|
+
[class.image-editor__stage--panning]="isPanning()"
|
|
363
|
+
(pointerdown)="onPointerDown($event)"
|
|
364
|
+
(pointermove)="onPointerMove($event)"
|
|
365
|
+
(pointerup)="onPointerUp($event)"
|
|
366
|
+
(pointercancel)="onPointerUp($event)"
|
|
367
|
+
(wheel)="onWheel($event)"
|
|
368
|
+
>
|
|
369
|
+
@if (sourceUrl(); as url) {
|
|
370
|
+
<img
|
|
371
|
+
class="image-editor__image"
|
|
372
|
+
[src]="url"
|
|
373
|
+
alt=""
|
|
374
|
+
draggable="false"
|
|
375
|
+
[style.width.px]="imageBox().width"
|
|
376
|
+
[style.height.px]="imageBox().height"
|
|
377
|
+
[style.transform]="imageBox().transform"
|
|
378
|
+
/>
|
|
379
|
+
}
|
|
380
|
+
</div>
|
|
381
|
+
<m-range
|
|
382
|
+
[config]="zoomConfig()"
|
|
383
|
+
[value]="zoomPercent()"
|
|
384
|
+
(valueChange)="onZoom($event)"
|
|
385
|
+
/>
|
|
386
|
+
<!-- asset: buttons/okay.yml. No cancel beside it: the Modal's own close
|
|
387
|
+
is the cancel, and it drops the image the same way. -->
|
|
388
|
+
<m-one-button
|
|
389
|
+
name="okay"
|
|
390
|
+
label="apply"
|
|
391
|
+
[disabled]="!source() || isApplying()"
|
|
392
|
+
(clicked)="apply()"
|
|
393
|
+
/>
|
|
394
|
+
</div>
|
|
395
|
+
`,
|
|
396
|
+
imports: [
|
|
397
|
+
ButtonComponent,
|
|
398
|
+
HeaderComponent,
|
|
399
|
+
RangeInputComponent
|
|
400
|
+
],
|
|
401
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
402
|
+
styles: ["@keyframes toggle-pulse{0%,to{transform:scale(1)}50%{transform:scale(1.02)}}@keyframes thumb-slide{0%{transform:translate(0) scale(1)}50%{transform:translate(.3em) scale(.9)}to{transform:translate(.6em) scale(1)}}:host{display:block}.image-editor{display:flex;flex-direction:column;gap:1rem;width:100%;min-width:0;padding:1.5rem}.image-editor__stage{position:relative;width:100%;max-width:calc((90vh - 16rem) * var(--m-image-editor-aspect, 1));margin-inline:auto;overflow:hidden;border:1px solid var(--m-border);background:var(--m-background-lite);cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}.image-editor__stage--panning{cursor:grabbing}.image-editor__image{position:absolute;top:0;left:0;max-width:none;transform-origin:0 0;pointer-events:none}\n"]
|
|
403
|
+
}]
|
|
404
|
+
}],
|
|
405
|
+
propDecorators: {
|
|
406
|
+
file: [{
|
|
407
|
+
type: i0.Input,
|
|
408
|
+
args: [{
|
|
409
|
+
isSignal: true,
|
|
410
|
+
alias: "file",
|
|
411
|
+
required: true
|
|
412
|
+
}]
|
|
413
|
+
}],
|
|
414
|
+
ratio: [{
|
|
415
|
+
type: i0.Input,
|
|
416
|
+
args: [{
|
|
417
|
+
isSignal: true,
|
|
418
|
+
alias: "ratio",
|
|
419
|
+
required: true
|
|
420
|
+
}]
|
|
421
|
+
}],
|
|
422
|
+
width: [{
|
|
423
|
+
type: i0.Input,
|
|
424
|
+
args: [{
|
|
425
|
+
isSignal: true,
|
|
426
|
+
alias: "width",
|
|
427
|
+
required: true
|
|
428
|
+
}]
|
|
429
|
+
}],
|
|
430
|
+
stage: [{
|
|
431
|
+
type: i0.ViewChild,
|
|
432
|
+
args: ["stage", { isSignal: true }]
|
|
433
|
+
}]
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
export { ImageEditorComponent };
|
|
437
|
+
|
|
438
|
+
//# sourceMappingURL=magmonium-one-image-editor-BX4eNd4u.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"magmonium-one-image-editor-BX4eNd4u.mjs","names":[],"sources":["../../../../libs/one/src/lib/shared/ui/input/ui/file-upload-input/image-editor.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n effect,\n ElementRef,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core';\nimport { ButtonComponent } from '../../../button/ui/button';\nimport { HeaderComponent } from '../../../header';\nimport { RangeInputComponent } from '../range/range';\nimport { InputType, RangeInput } from '../../model/input';\nimport { MODAL_REF, ModalRef } from '../../../../config/modal-ref';\nimport {\n clampFraming,\n cropRect,\n drawFramed,\n encodeFramed,\n Framing,\n FrameSize,\n initialFraming,\n MAX_ZOOM,\n MIN_ZOOM,\n outputSize,\n parseRatio,\n renameForType,\n} from './image-frame';\n\n/**\n * What an ImageEditor closes with. The framed file on an apply, `unreadable`\n * when the picture never decoded, and neither for a dismissal — a flat object\n * rather than a union, so the web-component build (no `strict`) reads it too.\n */\nexport type ImageEditorResult = { file?: File; unreadable?: boolean };\n\ntype Point = { x: number; y: number };\n\nconst WHEEL_ZOOM_RATE = 0.0015;\n\nconst distance = (a: Point, b: Point): number =>\n Math.hypot(a.x - b.x, a.y - b.y);\n\n@Component({\n selector: 'm-image-editor',\n template: `\n <div class=\"image-editor\">\n <m-header\n [level]=\"3\"\n label=\"edit-image-size\"\n [params]=\"sizeParams()\"\n />\n <div\n #stage\n class=\"image-editor__stage\"\n [style.aspect-ratio]=\"aspect()\"\n [style.--m-image-editor-aspect]=\"aspect()\"\n [class.image-editor__stage--panning]=\"isPanning()\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (wheel)=\"onWheel($event)\"\n >\n @if (sourceUrl(); as url) {\n <img\n class=\"image-editor__image\"\n [src]=\"url\"\n alt=\"\"\n draggable=\"false\"\n [style.width.px]=\"imageBox().width\"\n [style.height.px]=\"imageBox().height\"\n [style.transform]=\"imageBox().transform\"\n />\n }\n </div>\n <m-range\n [config]=\"zoomConfig()\"\n [value]=\"zoomPercent()\"\n (valueChange)=\"onZoom($event)\"\n />\n <!-- asset: buttons/okay.yml. No cancel beside it: the Modal's own close\n is the cancel, and it drops the image the same way. -->\n <m-one-button\n name=\"okay\"\n label=\"apply\"\n [disabled]=\"!source() || isApplying()\"\n (clicked)=\"apply()\"\n />\n </div>\n `,\n imports: [ButtonComponent, HeaderComponent, RangeInputComponent],\n styleUrl: './image-editor.sass',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ImageEditorComponent {\n readonly file = input.required<File>();\n readonly ratio = input.required<string>();\n readonly width = input.required<number>();\n\n readonly #modalRef = inject<ModalRef<ImageEditorResult | undefined>>(MODAL_REF);\n private readonly stage = viewChild<ElementRef<HTMLDivElement>>('stage');\n\n protected readonly source = signal<ImageBitmap | undefined>(undefined);\n protected readonly sourceUrl = signal<string | undefined>(undefined);\n protected readonly isApplying = signal(false);\n private readonly framing = signal<Framing>({ cx: 0, cy: 0, zoom: MIN_ZOOM });\n private readonly stageWidth = signal(0);\n private readonly pointers = new Map<number, Point>();\n protected readonly isPanning = signal(false);\n #settled = false;\n\n protected readonly zoomConfig = computed<RangeInput>(() => ({\n name: 'zoom',\n label: 'zoom',\n type: InputType.RANGE,\n min: MIN_ZOOM * 100,\n max: MAX_ZOOM * 100,\n }));\n\n protected readonly aspect = computed(() => parseRatio(this.ratio()) ?? 1);\n\n private readonly output = computed<FrameSize | undefined>(() =>\n outputSize(this.ratio(), this.width())\n );\n\n protected readonly sizeParams = computed(() => {\n const size = this.output();\n return {\n width: String(size?.width ?? ''),\n height: String(size?.height ?? ''),\n };\n });\n\n private readonly imageSize = computed<FrameSize | undefined>(() => {\n const bitmap = this.source();\n return bitmap ? { width: bitmap.width, height: bitmap.height } : undefined;\n });\n\n protected readonly zoomPercent = computed(() =>\n String(Math.round(this.framing().zoom * 100))\n );\n\n /** The whole picture laid out so the box shows exactly the crop. */\n protected readonly imageBox = computed(() => {\n const image = this.imageSize();\n const stage = this.stageWidth();\n if (!image || !stage) return { width: 0, height: 0, transform: 'none' };\n const rect = cropRect(image, this.aspect(), this.framing());\n const scale = stage / rect.width;\n return {\n width: image.width * scale,\n height: image.height * scale,\n transform: `translate(${-rect.x * scale}px, ${-rect.y * scale}px)`,\n };\n });\n\n private readonly load = effect((onCleanup) => {\n const file = this.file();\n let cancelled = false;\n const url = URL.createObjectURL(file);\n this.sourceUrl.set(url);\n // EXIF orientation applied here, so the geometry below and the <img> the\n // stage draws agree on which way is up.\n createImageBitmap(file, { imageOrientation: 'from-image' })\n .then((bitmap) => {\n if (cancelled) {\n bitmap.close();\n return;\n }\n this.framing.set(\n initialFraming({ width: bitmap.width, height: bitmap.height })\n );\n this.source.set(bitmap);\n })\n .catch(() => {\n if (!cancelled) this.settle({ unreadable: true });\n });\n onCleanup(() => {\n cancelled = true;\n URL.revokeObjectURL(url);\n this.source()?.close();\n });\n });\n\n private readonly measure = effect((onCleanup) => {\n const stage = this.stage()?.nativeElement;\n if (!stage) return;\n this.stageWidth.set(stage.clientWidth);\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(([entry]) =>\n this.stageWidth.set(entry.contentRect.width)\n );\n observer.observe(stage);\n onCleanup(() => observer.disconnect());\n });\n\n // The Modal's own close affordance destroys the body without a word, and a\n // dismissal is a cancel: whoever opened the editor is still waiting.\n private readonly reportDismissal = inject(DestroyRef).onDestroy(() =>\n this.settle(undefined)\n );\n\n protected onPointerDown = (event: PointerEvent): void => {\n if (!this.source()) return;\n (event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);\n this.pointers.set(event.pointerId, { x: event.clientX, y: event.clientY });\n this.isPanning.set(true);\n };\n\n protected onPointerMove = (event: PointerEvent): void => {\n const previous = this.pointers.get(event.pointerId);\n if (!previous) return;\n const next = { x: event.clientX, y: event.clientY };\n const others = [...this.pointers.entries()].filter(\n ([id]) => id !== event.pointerId\n );\n if (others.length === 1) {\n // Pinch: the gap between two fingers is the zoom.\n const anchor = others[0][1];\n const before = distance(previous, anchor);\n if (before > 0) this.zoomBy(distance(next, anchor) / before);\n } else if (others.length === 0) {\n this.panBy(next.x - previous.x, next.y - previous.y);\n }\n this.pointers.set(event.pointerId, next);\n };\n\n protected onPointerUp = (event: PointerEvent): void => {\n this.pointers.delete(event.pointerId);\n this.isPanning.set(this.pointers.size > 0);\n };\n\n protected onWheel = (event: WheelEvent): void => {\n if (!this.source()) return;\n event.preventDefault();\n this.zoomBy(Math.exp(-event.deltaY * WHEEL_ZOOM_RATE));\n };\n\n protected onZoom = (percent: string): void => {\n const zoom = Number(percent) / 100;\n if (!Number.isFinite(zoom)) return;\n this.updateFraming((framing) => ({ ...framing, zoom }));\n };\n\n protected apply = async (): Promise<void> => {\n const bitmap = this.source();\n const image = this.imageSize();\n const output = this.output();\n if (!bitmap || !image || !output || this.isApplying()) return;\n this.isApplying.set(true);\n try {\n const rect = cropRect(image, this.aspect(), this.framing());\n const blob = await encodeFramed(drawFramed(bitmap, rect, output));\n const original = this.file();\n this.settle({\n file: new File([blob], renameForType(original.name, blob.type), {\n type: blob.type,\n lastModified: Date.now(),\n }),\n });\n } catch {\n this.settle({ unreadable: true });\n }\n };\n\n /** Screen pixels dragged, moved into source pixels at the current scale. */\n private panBy = (dx: number, dy: number): void => {\n const image = this.imageSize();\n const stage = this.stageWidth();\n if (!image || !stage) return;\n const rect = cropRect(image, this.aspect(), this.framing());\n const perPixel = rect.width / stage;\n this.updateFraming((framing) => ({\n ...framing,\n cx: framing.cx - dx * perPixel,\n cy: framing.cy - dy * perPixel,\n }));\n };\n\n private zoomBy = (factor: number): void => {\n this.updateFraming((framing) => ({\n ...framing,\n zoom: framing.zoom * factor,\n }));\n };\n\n private updateFraming = (change: (framing: Framing) => Framing): void => {\n const image = this.imageSize();\n if (!image) return;\n this.framing.update((framing) =>\n clampFraming(image, this.aspect(), change(framing))\n );\n };\n\n private settle = (result: ImageEditorResult | undefined): void => {\n if (this.#settled) return;\n this.#settled = true;\n this.#modalRef.close(result);\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const EDITABLE_TYPES = [
|
|
2
|
+
"image/png",
|
|
3
|
+
"image/jpeg",
|
|
4
|
+
"image/webp"
|
|
5
|
+
];
|
|
6
|
+
const isEditableImage = (file) => EDITABLE_TYPES.includes(file.type.toLowerCase());
|
|
7
|
+
const parseRatio = (ratio) => {
|
|
8
|
+
if (typeof ratio !== "string") return void 0;
|
|
9
|
+
const match = /^\s*(\d+(?:\.\d+)?)\s*:\s*(\d+(?:\.\d+)?)\s*$/.exec(ratio);
|
|
10
|
+
if (!match) return void 0;
|
|
11
|
+
const width = Number(match[1]);
|
|
12
|
+
const height = Number(match[2]);
|
|
13
|
+
return width > 0 && height > 0 ? width / height : void 0;
|
|
14
|
+
};
|
|
15
|
+
const outputSize = (ratio, width) => {
|
|
16
|
+
const aspect = parseRatio(ratio);
|
|
17
|
+
const pixels = Math.round(Number(width));
|
|
18
|
+
if (!aspect || !Number.isFinite(pixels) || pixels < 1) return void 0;
|
|
19
|
+
return {
|
|
20
|
+
width: pixels,
|
|
21
|
+
height: Math.max(1, Math.round(pixels / aspect))
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
const coverCrop = (image, aspect) => image.width / image.height > aspect ? {
|
|
25
|
+
width: image.height * aspect,
|
|
26
|
+
height: image.height
|
|
27
|
+
} : {
|
|
28
|
+
width: image.width,
|
|
29
|
+
height: image.width / aspect
|
|
30
|
+
};
|
|
31
|
+
const initialFraming = (image) => ({
|
|
32
|
+
cx: image.width / 2,
|
|
33
|
+
cy: image.height / 2,
|
|
34
|
+
zoom: 1
|
|
35
|
+
});
|
|
36
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
37
|
+
const clampFraming = (image, aspect, framing) => {
|
|
38
|
+
const zoom = clamp(framing.zoom, 1, 4);
|
|
39
|
+
const cover = coverCrop(image, aspect);
|
|
40
|
+
const halfW = cover.width / zoom / 2;
|
|
41
|
+
const halfH = cover.height / zoom / 2;
|
|
42
|
+
return {
|
|
43
|
+
zoom,
|
|
44
|
+
cx: clamp(framing.cx, halfW, image.width - halfW),
|
|
45
|
+
cy: clamp(framing.cy, halfH, image.height - halfH)
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const cropRect = (image, aspect, framing) => {
|
|
49
|
+
const { cx, cy, zoom } = clampFraming(image, aspect, framing);
|
|
50
|
+
const cover = coverCrop(image, aspect);
|
|
51
|
+
const width = cover.width / zoom;
|
|
52
|
+
const height = cover.height / zoom;
|
|
53
|
+
return {
|
|
54
|
+
x: cx - width / 2,
|
|
55
|
+
y: cy - height / 2,
|
|
56
|
+
width,
|
|
57
|
+
height
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
const renameForType = (name, type) => {
|
|
61
|
+
const extension = type === "image/jpeg" ? "jpg" : type.split("/")[1] ?? "bin";
|
|
62
|
+
const dot = name.lastIndexOf(".");
|
|
63
|
+
return `${dot > 0 ? name.slice(0, dot) : name}.${extension}`;
|
|
64
|
+
};
|
|
65
|
+
const createCanvas = (size) => {
|
|
66
|
+
const canvas = document.createElement("canvas");
|
|
67
|
+
canvas.width = Math.max(1, Math.round(size.width));
|
|
68
|
+
canvas.height = Math.max(1, Math.round(size.height));
|
|
69
|
+
return canvas;
|
|
70
|
+
};
|
|
71
|
+
const smoothContext = (canvas) => {
|
|
72
|
+
const context = canvas.getContext("2d");
|
|
73
|
+
if (!context) throw new Error("2d canvas unavailable");
|
|
74
|
+
context.imageSmoothingEnabled = true;
|
|
75
|
+
context.imageSmoothingQuality = "high";
|
|
76
|
+
return context;
|
|
77
|
+
};
|
|
78
|
+
const drawFramed = (source, rect, output) => {
|
|
79
|
+
let from = source;
|
|
80
|
+
let area = rect;
|
|
81
|
+
while (area.width >= output.width * 2 && area.height >= output.height * 2) {
|
|
82
|
+
const step = createCanvas({
|
|
83
|
+
width: area.width / 2,
|
|
84
|
+
height: area.height / 2
|
|
85
|
+
});
|
|
86
|
+
smoothContext(step).drawImage(from, area.x, area.y, area.width, area.height, 0, 0, step.width, step.height);
|
|
87
|
+
from = step;
|
|
88
|
+
area = {
|
|
89
|
+
x: 0,
|
|
90
|
+
y: 0,
|
|
91
|
+
width: step.width,
|
|
92
|
+
height: step.height
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const canvas = createCanvas(output);
|
|
96
|
+
smoothContext(canvas).drawImage(from, area.x, area.y, area.width, area.height, 0, 0, canvas.width, canvas.height);
|
|
97
|
+
return canvas;
|
|
98
|
+
};
|
|
99
|
+
const ENCODE_QUALITY = .9;
|
|
100
|
+
const toBlob = (canvas, type) => new Promise((resolve) => canvas.toBlob(resolve, type, ENCODE_QUALITY));
|
|
101
|
+
const hasTransparency = (canvas) => {
|
|
102
|
+
const { data } = smoothContext(canvas).getImageData(0, 0, canvas.width, canvas.height);
|
|
103
|
+
for (let i = 3; i < data.length; i += 4) if (data[i] < 255) return true;
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
const encodeFramed = async (canvas) => {
|
|
107
|
+
const webp = await toBlob(canvas, "image/webp");
|
|
108
|
+
if (webp?.type === "image/webp") return webp;
|
|
109
|
+
const type = hasTransparency(canvas) ? "image/png" : "image/jpeg";
|
|
110
|
+
const fallback = await toBlob(canvas, type);
|
|
111
|
+
if (!fallback) throw new Error("canvas encode failed");
|
|
112
|
+
return fallback;
|
|
113
|
+
};
|
|
114
|
+
export { initialFraming as a, parseRatio as c, encodeFramed as i, renameForType as l, cropRect as n, isEditableImage as o, drawFramed as r, outputSize as s, clampFraming as t };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=magmonium-one-image-frame-BTSaUSAi.mjs.map
|