mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
feat(color): replace native color input with swatch picker
Replace the browser's native <input type="color"> with a custom color picker component showing a 5x4 grid of preset swatches and a custom color button that opens the native picker. - New standalone InputColorPickerComponent with overlay panel - 19 preset colors in rainbow hue order + 1 custom color button - Formly ColorInputComponent wraps the new picker - dialog-create-tag uses the new picker directly - Escape key and backdrop click to close overlay - Fixed positioning to avoid dialog overflow clipping
This commit is contained in:
parent
061acf4124
commit
cd12556d49
13 changed files with 674 additions and 138 deletions
423
package-lock.json
generated
423
package-lock.json
generated
|
|
@ -645,6 +645,268 @@
|
|||
"vite": "^6.0.0 || ^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/@vitest/expect": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.1.tgz",
|
||||
"integrity": "sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.1.0",
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/spy": "4.1.1",
|
||||
"@vitest/utils": "4.1.1",
|
||||
"chai": "^6.2.2",
|
||||
"tinyrainbow": "^3.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/@vitest/pretty-format": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.1.tgz",
|
||||
"integrity": "sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^3.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/@vitest/runner": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.1.tgz",
|
||||
"integrity": "sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/utils": "4.1.1",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/@vitest/snapshot": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.1.tgz",
|
||||
"integrity": "sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.1",
|
||||
"@vitest/utils": "4.1.1",
|
||||
"magic-string": "^0.30.21",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/@vitest/spy": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.1.tgz",
|
||||
"integrity": "sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/@vitest/utils": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.1.tgz",
|
||||
"integrity": "sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.1",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"tinyrainbow": "^3.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/chai": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/es-module-lexer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
|
||||
"integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/std-env": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz",
|
||||
"integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/tinyrainbow": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
|
||||
"integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/vitest": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.1.tgz",
|
||||
"integrity": "sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.1.1",
|
||||
"@vitest/mocker": "4.1.1",
|
||||
"@vitest/pretty-format": "4.1.1",
|
||||
"@vitest/runner": "4.1.1",
|
||||
"@vitest/snapshot": "4.1.1",
|
||||
"@vitest/spy": "4.1.1",
|
||||
"@vitest/utils": "4.1.1",
|
||||
"es-module-lexer": "^2.0.0",
|
||||
"expect-type": "^1.3.0",
|
||||
"magic-string": "^0.30.21",
|
||||
"obug": "^2.1.1",
|
||||
"pathe": "^2.0.3",
|
||||
"picomatch": "^4.0.3",
|
||||
"std-env": "^4.0.0-rc.1",
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^1.0.2",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyrainbow": "^3.0.3",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"vitest": "vitest.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.0.0 || ^22.0.0 || >=24.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "4.1.1",
|
||||
"@vitest/browser-preview": "4.1.1",
|
||||
"@vitest/browser-webdriverio": "4.1.1",
|
||||
"@vitest/ui": "4.1.1",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@edge-runtime/vm": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-playwright": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-preview": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-webdriverio": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/ui": {
|
||||
"optional": true
|
||||
},
|
||||
"happy-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"jsdom": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/vitest/node_modules/@vitest/mocker": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.1.tgz",
|
||||
"integrity": "sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/spy": "4.1.1",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.21"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"msw": "^2.4.9",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"msw": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-webpack": {
|
||||
"version": "0.2102.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2102.3.tgz",
|
||||
|
|
@ -3450,6 +3712,24 @@
|
|||
"node": ">=10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/asar/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@electron/asar/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/asar/node_modules/commander": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
|
||||
|
|
@ -3483,19 +3763,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@electron/asar/node_modules/minimatch": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/fuses": {
|
||||
|
|
@ -3912,17 +4189,34 @@
|
|||
"node": ">=16.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/universal/node_modules/minimatch": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"node_modules/@electron/universal/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@electron/universal/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/universal/node_modules/minimatch": {
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
|
|
@ -12675,6 +12969,23 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cacache/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cacache/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cacache/node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
|
|
@ -12704,6 +13015,22 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/cacache/node_modules/glob/node_modules/minimatch": {
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/cacache/node_modules/jackspeak": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||
|
|
@ -12727,22 +13054,6 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/cacache/node_modules/minimatch": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/cacache/node_modules/path-scurry": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||
|
|
@ -17328,20 +17639,34 @@
|
|||
"minimatch": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/minimatch": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"node_modules/filelist/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/filelist/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/minimatch": {
|
||||
"version": "5.1.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
|
||||
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
|
|
@ -23232,6 +23557,19 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/obug": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
|
||||
"integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/sxzz",
|
||||
"https://opencollective.com/debug"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/on-exit-leak-free": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
||||
|
|
@ -24552,6 +24890,7 @@
|
|||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
<mat-form-field>
|
||||
<mat-label>{{ to.label }}</mat-label>
|
||||
<input
|
||||
type="color"
|
||||
class="color-input"
|
||||
[formControl]="formControl"
|
||||
[formlyAttributes]="field"
|
||||
(focus)="onFocus($event)"
|
||||
matInput
|
||||
/>
|
||||
</mat-form-field>
|
||||
<input-color-picker
|
||||
[label]="to.label"
|
||||
[value]="formControl.value"
|
||||
(valueChange)="onColorChange($event)"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
mat-form-field {
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: var(--s) 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,19 @@
|
|||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
||||
import { FieldType, FormlyModule } from '@ngx-formly/core';
|
||||
import { IS_FIREFOX } from '../../../util/is-firefox';
|
||||
import { IS_MOBILE } from '../../../util/is-mobile';
|
||||
import { InputColorPickerComponent } from '../../../ui/input-color-picker/input-color-picker.component';
|
||||
|
||||
/**
|
||||
* This component deliberately avoids Formly's field type abstractions for the
|
||||
* Material UI components because the form field wrapper implementation uses
|
||||
* a focus monitoring service that is counterproductive for certain native
|
||||
* color input controls.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'color-input',
|
||||
templateUrl: './color-input.component.html',
|
||||
styleUrls: ['./color-input.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [FormlyModule, MatFormField, MatInput, MatLabel, ReactiveFormsModule],
|
||||
imports: [FormlyModule, ReactiveFormsModule, InputColorPickerComponent],
|
||||
})
|
||||
export class ColorInputComponent extends FieldType {
|
||||
onFocus(event: FocusEvent): void {
|
||||
if (!IS_FIREFOX || IS_MOBILE) return;
|
||||
// Desktop Firefox oddly fires another focus event when the native color
|
||||
// input closes rather than a blur event, so determine whether a value
|
||||
// change has occurred and force a blur to have it persisted
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.value !== this.formControl.value) {
|
||||
input.blur();
|
||||
}
|
||||
onColorChange(color: string): void {
|
||||
this.formControl.setValue(color);
|
||||
this.formControl.markAsTouched();
|
||||
this.formControl.markAsDirty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,26 @@
|
|||
const WORK_CONTEXT_COLORS: string[] = [
|
||||
'#29a1aa',
|
||||
'#a05db1',
|
||||
'#ef5350',
|
||||
'#ec407a',
|
||||
'#ab47bc',
|
||||
'#7e57c2',
|
||||
'#5c6bc0',
|
||||
'#42a5f5',
|
||||
'#29b6f6',
|
||||
'#26c6da',
|
||||
'#26a69a',
|
||||
'#66bb6a',
|
||||
'#9ccc65',
|
||||
'#d4e157',
|
||||
'#ffee58',
|
||||
'#ffca28',
|
||||
'#ffa726',
|
||||
'#ff7043',
|
||||
'#8d6e63',
|
||||
'#bdbdbd',
|
||||
'#78909c',
|
||||
export const PRESET_COLORS: readonly string[] = [
|
||||
'#ef5350', // red
|
||||
'#ff7043', // deep orange
|
||||
'#ffa726', // orange
|
||||
'#ffca28', // amber
|
||||
'#ffee58', // yellow
|
||||
'#d4e157', // lime
|
||||
'#9ccc65', // light green
|
||||
'#66bb6a', // green
|
||||
'#29a1aa', // teal
|
||||
'#26c6da', // cyan
|
||||
'#29b6f6', // light blue
|
||||
'#42a5f5', // blue
|
||||
'#5c6bc0', // indigo
|
||||
'#7e57c2', // deep purple
|
||||
'#a05db1', // purple
|
||||
'#ab47bc', // purple alt
|
||||
'#ec407a', // pink
|
||||
'#8d6e63', // brown
|
||||
'#78909c', // blue grey
|
||||
];
|
||||
|
||||
export const getRandomWorkContextColor = (): string => {
|
||||
const index = Math.floor(Math.random() * WORK_CONTEXT_COLORS.length);
|
||||
return WORK_CONTEXT_COLORS[index];
|
||||
const index = Math.floor(Math.random() * PRESET_COLORS.length);
|
||||
return PRESET_COLORS[index];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@
|
|||
<span>{{ T.PDS.SUMMARY_FOR | translate: { dayStr: dayStr } }}</span>
|
||||
}
|
||||
</h1>
|
||||
<div
|
||||
class="daily-summary-summary"
|
||||
[class.is-loading-archive]="!isArchiveLoaded()"
|
||||
>
|
||||
<div class="daily-summary-summary">
|
||||
<div class="summary-point">
|
||||
<mat-icon>schedule</mat-icon>
|
||||
<div class="summary-text">
|
||||
|
|
@ -165,10 +162,8 @@
|
|||
[isForToday]="isForToday"
|
||||
[isShowYesterday]="isForToday && isIncludeYesterday"
|
||||
></task-summary-tables>
|
||||
} @else if (isArchiveLoaded()) {
|
||||
<p>{{ T.PDS.NO_TASKS | translate }}</p>
|
||||
} @else {
|
||||
<mat-spinner style="margin: auto"></mat-spinner>
|
||||
<p>{{ T.PDS.NO_TASKS | translate }}</p>
|
||||
}
|
||||
@if (tasks?.length) {
|
||||
<tasks-by-tag
|
||||
|
|
|
|||
|
|
@ -74,11 +74,6 @@ $summary-point-inner-margin: var(--s);
|
|||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
transition: opacity 0.2s ease;
|
||||
|
||||
&.is-loading-archive {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 5px;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
linkedSignal,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
signal,
|
||||
Signal,
|
||||
} from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
|
|
@ -30,7 +29,6 @@ import {
|
|||
switchMap,
|
||||
take,
|
||||
takeUntil,
|
||||
tap,
|
||||
timeout,
|
||||
withLatestFrom,
|
||||
} from 'rxjs/operators';
|
||||
|
|
@ -67,6 +65,7 @@ import { MsToClockStringPipe } from '../../ui/duration/ms-to-clock-string.pipe';
|
|||
import { InlineInputComponent } from '../../ui/inline-input/inline-input.component';
|
||||
import { InlineMarkdownComponent } from '../../ui/inline-markdown/inline-markdown.component';
|
||||
import { MomentFormatPipe } from '../../ui/pipes/moment-format.pipe';
|
||||
import { IS_TOUCH_ONLY } from '../../util/is-touch-only';
|
||||
import { getPluralKey } from '../../util/get-plural-key';
|
||||
import { shareReplayUntil } from '../../util/share-replay-until';
|
||||
import { unToggleCheckboxesInMarkdownTxt } from '../../util/untoggle-checkboxes-in-markdown-txt';
|
||||
|
|
@ -170,8 +169,6 @@ export class DailySummaryComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
shareReplay(1),
|
||||
);
|
||||
|
||||
isArchiveLoaded = signal(false);
|
||||
|
||||
hasTasksForToday$: Observable<boolean> = this.tasksWorkedOnOrDoneOrRepeatableFlat$.pipe(
|
||||
map((tasks) => tasks && !!tasks.length),
|
||||
);
|
||||
|
|
@ -314,16 +311,12 @@ export class DailySummaryComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
return;
|
||||
}
|
||||
|
||||
this.tasksWorkedOnOrDoneOrRepeatableFlat$
|
||||
.pipe(
|
||||
first((tasks) => tasks.length > 0),
|
||||
takeUntil(this._onDestroy$),
|
||||
)
|
||||
.subscribe(() => {
|
||||
this._startCelebrationTimeout = window.setTimeout(() => {
|
||||
this._celebrate();
|
||||
}, 300);
|
||||
});
|
||||
this._startCelebrationTimeout = window.setTimeout(
|
||||
() => {
|
||||
this._celebrate();
|
||||
},
|
||||
IS_TOUCH_ONLY ? 1500 : 500,
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
|
@ -500,7 +493,7 @@ export class DailySummaryComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
}
|
||||
|
||||
private _getDailySummaryTasksFlat$(dayStr: string): Observable<Task[]> {
|
||||
this.isArchiveLoaded.set(false);
|
||||
// TODO make more performant!!
|
||||
const _isWorkedOnDoneOrDueToday = (() => {
|
||||
if (this.isIncludeYesterday) {
|
||||
const yesterday = new Date();
|
||||
|
|
@ -621,7 +614,6 @@ export class DailySummaryComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
withLatestFrom(this.workContextService.activeWorkContextTypeAndId$),
|
||||
map(_mapEntities),
|
||||
map(_mapFilterToFlatToday),
|
||||
tap(() => this.isArchiveLoaded.set(true)),
|
||||
);
|
||||
|
||||
const todayTasks: Observable<TaskWithSubTasks[]> =
|
||||
|
|
@ -631,10 +623,9 @@ export class DailySummaryComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
map(_mapFilterToFlatOrRepeatToday),
|
||||
);
|
||||
|
||||
return combineLatest([
|
||||
todayTasks,
|
||||
archiveTasks.pipe(startWith([] as TaskWithSubTasks[])),
|
||||
]).pipe(map(([today, archive]) => today.concat(archive)));
|
||||
return combineLatest([todayTasks, archiveTasks]).pipe(
|
||||
map(([t1, t2]) => t1.concat(t2)),
|
||||
);
|
||||
}
|
||||
|
||||
private _celebrate(): void {
|
||||
|
|
|
|||
|
|
@ -44,15 +44,11 @@
|
|||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>{{ T.F.TAG.FORM_BASIC.L_COLOR | translate }}</mat-label>
|
||||
<input
|
||||
[(ngModel)]="color"
|
||||
name="color"
|
||||
matInput
|
||||
type="color"
|
||||
/>
|
||||
</mat-form-field>
|
||||
<input-color-picker
|
||||
[label]="T.F.TAG.FORM_BASIC.L_COLOR | translate"
|
||||
[value]="color"
|
||||
(valueChange)="color = $event"
|
||||
/>
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { DEFAULT_TAG_COLOR } from '../../features/work-context/work-context.cons
|
|||
import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autocomplete';
|
||||
import { MatOption } from '@angular/material/core';
|
||||
import { MaterialIconsLoaderService } from '../material-icons-loader.service';
|
||||
import { InputColorPickerComponent } from '../input-color-picker/input-color-picker.component';
|
||||
|
||||
export interface CreateTagData {
|
||||
title?: string;
|
||||
|
|
@ -47,6 +48,7 @@ export interface CreateTagData {
|
|||
MatAutocomplete,
|
||||
MatAutocompleteTrigger,
|
||||
MatOption,
|
||||
InputColorPickerComponent,
|
||||
],
|
||||
})
|
||||
export class DialogCreateTagComponent {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
@if (label()) {
|
||||
<label class="color-label">{{ label() }}</label>
|
||||
}
|
||||
|
||||
<button
|
||||
#trigger
|
||||
type="button"
|
||||
class="color-trigger"
|
||||
[style.background-color]="value()"
|
||||
(click)="toggle()"
|
||||
>
|
||||
</button>
|
||||
|
||||
@if (isOpen()) {
|
||||
<div class="backdrop" (click)="isOpen.set(false)"></div>
|
||||
<div
|
||||
class="color-panel"
|
||||
[style.position]="'fixed'"
|
||||
[style.top]="panelTop"
|
||||
[style.left]="panelLeft"
|
||||
>
|
||||
<div class="color-grid">
|
||||
@for (color of presetColors; track color) {
|
||||
<button
|
||||
type="button"
|
||||
class="color-swatch"
|
||||
[style.background-color]="color"
|
||||
[class.selected]="value() === color"
|
||||
(click)="selectColor(color)"
|
||||
>
|
||||
@if (value() === color) {
|
||||
<mat-icon>check</mat-icon>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
<button
|
||||
type="button"
|
||||
class="color-swatch custom-color-btn"
|
||||
[style.background-color]="isPresetColor() ? '' : value()"
|
||||
[class.selected]="!isPresetColor()"
|
||||
(click)="openNativePicker()"
|
||||
>
|
||||
@if (!isPresetColor()) {
|
||||
<mat-icon>check</mat-icon>
|
||||
} @else {
|
||||
<mat-icon>colorize</mat-icon>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<input
|
||||
#nativeInput
|
||||
type="color"
|
||||
class="native-color-input"
|
||||
[value]="value()"
|
||||
(change)="onNativeChange($event)"
|
||||
/>
|
||||
101
src/app/ui/input-color-picker/input-color-picker.component.scss
Normal file
101
src/app/ui/input-color-picker/input-color-picker.component.scss
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
:host {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin-bottom: var(--s);
|
||||
}
|
||||
|
||||
.color-label {
|
||||
display: block;
|
||||
margin-bottom: var(--s);
|
||||
color: var(--text-color-muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.color-trigger {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--text-color-muted);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition: var(--transition-fast);
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: calc(var(--z-backdrop) - 1);
|
||||
}
|
||||
|
||||
.color-panel {
|
||||
padding: var(--s);
|
||||
background: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--whiteframe-shadow-8dp);
|
||||
z-index: var(--z-backdrop);
|
||||
}
|
||||
|
||||
.color-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: var(--s-half);
|
||||
}
|
||||
|
||||
.color-swatch {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
transition: var(--transition-fast);
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: var(--text-color);
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-color-btn {
|
||||
background-color: var(--card-bg);
|
||||
border-color: var(--text-color-muted);
|
||||
border-style: dashed;
|
||||
|
||||
mat-icon {
|
||||
color: var(--text-color-muted);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-style: solid;
|
||||
border-color: var(--text-color);
|
||||
|
||||
mat-icon {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.native-color-input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
input,
|
||||
output,
|
||||
signal,
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
import { PRESET_COLORS } from '../../features/work-context/work-context-color';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
|
||||
@Component({
|
||||
selector: 'input-color-picker',
|
||||
templateUrl: './input-color-picker.component.html',
|
||||
styleUrls: ['./input-color-picker.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [MatIcon],
|
||||
})
|
||||
export class InputColorPickerComponent {
|
||||
readonly value = input<string>('#000000');
|
||||
readonly label = input<string>('');
|
||||
readonly valueChange = output<string>();
|
||||
|
||||
readonly presetColors = PRESET_COLORS;
|
||||
readonly nativeInput = viewChild<ElementRef<HTMLInputElement>>('nativeInput');
|
||||
readonly trigger = viewChild<ElementRef<HTMLButtonElement>>('trigger');
|
||||
readonly isOpen = signal(false);
|
||||
readonly isPresetColor = computed(() => this.presetColors.includes(this.value()));
|
||||
|
||||
panelTop = '';
|
||||
panelLeft = '';
|
||||
|
||||
@HostListener('document:keydown.escape')
|
||||
onEscape(): void {
|
||||
if (this.isOpen()) {
|
||||
this.isOpen.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
toggle(): void {
|
||||
if (!this.isOpen()) {
|
||||
this._updatePanelPosition();
|
||||
}
|
||||
this.isOpen.update((v) => !v);
|
||||
}
|
||||
|
||||
selectColor(color: string): void {
|
||||
this.valueChange.emit(color);
|
||||
this.isOpen.set(false);
|
||||
}
|
||||
|
||||
openNativePicker(): void {
|
||||
this.isOpen.set(false);
|
||||
this.nativeInput()?.nativeElement.click();
|
||||
}
|
||||
|
||||
onNativeChange(event: Event): void {
|
||||
const el = event.target as HTMLInputElement;
|
||||
this.valueChange.emit(el.value);
|
||||
}
|
||||
|
||||
private _updatePanelPosition(): void {
|
||||
const rect = this.trigger()?.nativeElement.getBoundingClientRect();
|
||||
if (!rect) return;
|
||||
|
||||
const panelWidth = 196;
|
||||
const spaceRight = window.innerWidth - rect.left;
|
||||
const left =
|
||||
spaceRight >= panelWidth ? rect.left : window.innerWidth - panelWidth - 8;
|
||||
|
||||
this.panelTop = `${rect.bottom + 4}px`;
|
||||
this.panelLeft = `${left}px`;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue