diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/LuceneSearchDefinitionExtensions.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/LuceneSearchDefinitionExtensions.cs index f5ef00aaf8..c5dc8293a2 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/LuceneSearchDefinitionExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/LuceneSearchDefinitionExtensions.cs @@ -35,7 +35,7 @@ private sealed class DelegatedPipelineStageDefinition( string operatorName, Func, RenderedPipelineStageDefinition> renderer) - : PipelineStageDefinition + : PipelineStageDefinition { public override string OperatorName { get; } = operatorName; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentCollaborationHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentCollaborationHandler.cs index 64ec64870e..2d476b053f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentCollaborationHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentCollaborationHandler.cs @@ -74,7 +74,7 @@ await currentManager.UpdateDocAsync(notificationsContext, doc => var commentValue = new Comment(clock.GetCurrentInstant(), actor, text, url, skipHandlers); var commentJson = jsonSerializer.Serialize(commentValue); - stream.InsertRange(transaction, stream.Length, InputFactory.FromJson(commentJson)); + stream.InsertRange(transaction, stream.Length(transaction), InputFactory.FromJson(commentJson)); } }, ct); } @@ -157,7 +157,7 @@ await @event.Source.UpdateDocAsync(@event.Context, (doc) => var eventBody = Envelope.Create(commentEvent); var eventData = eventFormatter.ToEventData(eventBody, Guid.NewGuid()); - await eventStore.AppendAsync(Guid.NewGuid(), streamName, EtagVersion.Any, new List { eventData }); + await eventStore.AppendAsync(Guid.NewGuid(), streamName, EtagVersion.Any, [eventData]); foreach (var mentionedUser in commentEvent.Mentions.OrEmpty()) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index 1e996119f7..c895f072a6 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -35,8 +35,9 @@ - - + + + diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs index f6690de39b..eef603a905 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs @@ -29,7 +29,6 @@ namespace Squidex.Areas.Api.Controllers.Assets; public sealed class AssetsController( ICommandBus commandBus, IAssetQueryService assetQuery, - IAssetUsageTracker assetUsageTracker, ITagService tagService, AssetTusRunner assetTusRunner) : ApiController(commandBus) diff --git a/backend/src/Squidex/Pipeline/Squid/SquidMiddleware.cs b/backend/src/Squidex/Pipeline/Squid/SquidMiddleware.cs index 0f810f2df9..9d40042be4 100644 --- a/backend/src/Squidex/Pipeline/Squid/SquidMiddleware.cs +++ b/backend/src/Squidex/Pipeline/Squid/SquidMiddleware.cs @@ -11,7 +11,9 @@ namespace Squidex.Pipeline.Squid; +#pragma warning disable CS9113 // Parameter is unread. public sealed class SquidMiddleware(RequestDelegate next) +#pragma warning restore CS9113 // Parameter is unread. { private readonly string squidHappyLG = LoadSvg("happy"); private readonly string squidHappySM = LoadSvg("happy-sm"); @@ -97,7 +99,7 @@ void Replace(string source, string value) context.Response.StatusCode = 200; context.Response.ContentType = "image/svg+xml"; - context.Response.Headers["Cache-Control"] = "public, max-age=604800"; + context.Response.Headers.CacheControl = "public, max-age=604800"; await context.Response.WriteAsync(svg, context.RequestAborted); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index aa68823caa..46286206af 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -35,7 +35,7 @@ - + diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerProcessorTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerProcessorTests.cs index 08826fbe81..10047b8025 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerProcessorTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerProcessorTests.cs @@ -19,7 +19,7 @@ public sealed class MyEventConsumerProcessor( IEventFormatter eventFormatter, IEventStore eventStore, ILogger log) - : EventConsumerProcessor(persistenceFactory, eventConsumer, eventFormatter, eventStore, log) + : EventConsumerProcessor(persistenceFactory, eventConsumer, eventFormatter, eventStore, log) { public IEventSubscriber? Subscriber { get; set; } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3d833a4280..f6533e98c7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,14 +24,9 @@ "@graphiql/toolkit": "^0.9.1", "@iharbeck/ngx-virtual-scroller": "^17.0.2", "@lithiumjs/angular": "^8.0.0", - "@lithiumjs/ngx-virtual-scroll": "^0.3.2", - "@marker.io/browser": "^0.19.0", - "@types/ace": "^0.0.52", "ace-builds": "^1.34.2", "angular-gridster2": "18.0.1", - "angular-mentions": "1.5.0", "bootstrap": "5.2.3", - "copy-webpack-plugin": "^12.0.2", "core-js": "3.37.1", "cropperjs": "2.0.0-alpha.1", "date-fns": "3.6.0", @@ -39,12 +34,10 @@ "graphql": "16.8.1", "graphql-ws": "^5.16.0", "image-focus": "1.2.1", - "keycharm": "0.4.0", "leaflet": "1.9.4", "leaflet-control-geocoder": "2.4.0", "marked": "12.0.2", "mersenne-twister": "1.1.0", - "moment": "^2.30.1", "mousetrap": "1.6.5", "ng2-charts": "^6.0.1", "ngx-color-picker": "16.0.0", @@ -56,8 +49,8 @@ "react": "18.3.1", "react-dom": "18.3.1", "rxjs": "7.8.1", - "simplemde": "1.11.2", "slugify": "1.6.6", + "textarea-caret": "github:component/textarea-caret-position", "tslib": "2.6.2", "tui-calendar": "^1.15.3", "typemoq": "^2.1.0", @@ -88,6 +81,7 @@ "@storybook/addon-links": "^8.1.5", "@storybook/angular": "^8.1.5", "@storybook/testing-library": "^0.2.2", + "@types/ace": "^0.0.52", "@types/codemirror": "5.60.15", "@types/core-js": "2.5.8", "@types/jasmine": "5.1.4", @@ -97,12 +91,10 @@ "@types/node": "20.12.13", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@types/simplemde": "1.11.11", - "@types/tapable": "2.2.7", "@types/ws": "8.5.10", "@typescript-eslint/eslint-plugin": "^7.11.0", "@typescript-eslint/parser": "^7.11.0", - "@webcomponents/custom-elements": "^1.6.0", + "copy-webpack-plugin": "^12.0.2", "eslint": "^8.57.0", "eslint-config-airbnb-typescript": "18.0.0", "eslint-plugin-deprecation": "^2.0.0", @@ -4930,6 +4922,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -5052,20 +5045,6 @@ "typescript": ">=4.8.0" } }, - "node_modules/@lithiumjs/ngx-virtual-scroll": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@lithiumjs/ngx-virtual-scroll/-/ngx-virtual-scroll-0.3.2.tgz", - "integrity": "sha512-4cGuG8n5rdsgziaorKrKQ5Rew/+BNyBfmMEffgxi+2NLRvIZusuDgEEJBIk9WK09gPTg+sEwpiWW1SmKL2iWnw==", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": "8.x.x - 18.x.x", - "@angular/core": "8.x.x - 18.x.x", - "@lithiumjs/angular": ">=7.0.0", - "rxjs": "6.x.x - 7.x.x" - } - }, "node_modules/@ljharb/through": { "version": "2.3.13", "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", @@ -5156,11 +5135,6 @@ "win32" ] }, - "node_modules/@marker.io/browser": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@marker.io/browser/-/browser-0.19.0.tgz", - "integrity": "sha512-tK0FN5uMKw3cyUYECWstaMW/5qoz9AhMp21GWR1ppJcIFaAu8k0lyHvsWFzqnHDfaoTs3dBF52DjarQQB74ipw==" - }, "node_modules/@mdx-js/react": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", @@ -6870,6 +6844,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, "engines": { "node": ">=18" }, @@ -10190,7 +10165,8 @@ "node_modules/@types/ace": { "version": "0.0.52", "resolved": "https://registry.npmjs.org/@types/ace/-/ace-0.0.52.tgz", - "integrity": "sha512-YPF9S7fzpuyrxru+sG/rrTpZkC6gpHBPF14W3x70kqVOD+ks6jkYLapk4yceh36xej7K4HYxcyz9ZDQ2lTvwgQ==" + "integrity": "sha512-YPF9S7fzpuyrxru+sG/rrTpZkC6gpHBPF14W3x70kqVOD+ks6jkYLapk4yceh36xej7K4HYxcyz9ZDQ2lTvwgQ==", + "dev": true }, "node_modules/@types/aria-query": { "version": "5.0.1", @@ -10345,6 +10321,7 @@ "version": "8.44.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", + "dev": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -10352,6 +10329,7 @@ }, "node_modules/@types/eslint-scope": { "version": "3.7.4", + "dev": true, "license": "MIT", "dependencies": { "@types/eslint": "*", @@ -10429,7 +10407,8 @@ "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", @@ -10477,6 +10456,7 @@ "version": "20.12.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.13.tgz", "integrity": "sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA==", + "devOptional": true, "dependencies": { "undici-types": "~5.26.4" } @@ -10585,12 +10565,6 @@ "@types/send": "*" } }, - "node_modules/@types/simplemde": { - "version": "1.11.11", - "resolved": "https://registry.npmjs.org/@types/simplemde/-/simplemde-1.11.11.tgz", - "integrity": "sha512-H3ml0YJtx9XMs2pGHwQy/UHzkg9W3Ke3QikQ/jHhEZj1dfmPbWqIvaNyNrt3dtiVlztWhMgK02+Rmoem+Jot/Q==", - "dev": true - }, "node_modules/@types/sockjs": { "version": "0.3.36", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", @@ -10600,15 +10574,6 @@ "@types/node": "*" } }, - "node_modules/@types/tapable": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-2.2.7.tgz", - "integrity": "sha512-D6QzACV9vNX3r8HQQNTOnpG+Bv1rko+yEA82wKs3O9CQ5+XW7HI7TED17/UE7+5dIxyxZIWTxKbsBeF6uKFCwA==", - "dev": true, - "dependencies": { - "tapable": "^2.2.0" - } - }, "node_modules/@types/tern": { "version": "0.23.4", "license": "MIT", @@ -11160,6 +11125,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -11168,22 +11134,26 @@ "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -11193,12 +11163,14 @@ "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -11210,6 +11182,7 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -11218,6 +11191,7 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -11225,12 +11199,14 @@ "node_modules/@webassemblyjs/utf8": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -11246,6 +11222,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", @@ -11258,6 +11235,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -11269,6 +11247,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", @@ -11282,17 +11261,12 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, - "node_modules/@webcomponents/custom-elements": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", - "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==", - "dev": true - }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -11304,12 +11278,14 @@ "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true }, "node_modules/@yarnpkg/esbuild-plugin-pnp": { "version": "3.0.0-rc.15", @@ -11446,6 +11422,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -11457,6 +11434,7 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, "peerDependencies": { "acorn": "^8" } @@ -11557,6 +11535,7 @@ "version": "8.13.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", @@ -11570,6 +11549,7 @@ }, "node_modules/ajv-formats": { "version": "2.1.1", + "dev": true, "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -11585,6 +11565,7 @@ }, "node_modules/ajv-keywords": { "version": "5.1.0", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" @@ -11606,17 +11587,6 @@ "rxjs": "^7.0.0" } }, - "node_modules/angular-mentions": { - "version": "1.5.0", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": ">=7.2.0", - "@angular/core": ">=7.2.0" - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -12525,6 +12495,7 @@ }, "node_modules/buffer-from": { "version": "1.1.2", + "dev": true, "license": "MIT" }, "node_modules/bundle-name": { @@ -13008,6 +12979,7 @@ }, "node_modules/chrome-trace-event": { "version": "1.0.3", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0" @@ -13173,13 +13145,6 @@ "@types/tern": "*" } }, - "node_modules/codemirror-spell-checker": { - "version": "1.1.2", - "license": "MIT", - "dependencies": { - "typo-js": "*" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -13234,6 +13199,7 @@ }, "node_modules/commander": { "version": "2.20.3", + "dev": true, "license": "MIT" }, "node_modules/common-path-prefix": { @@ -13456,6 +13422,7 @@ "version": "12.0.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "dev": true, "dependencies": { "fast-glob": "^3.3.2", "glob-parent": "^6.0.1", @@ -13477,6 +13444,7 @@ }, "node_modules/copy-webpack-plugin/node_modules/glob-parent": { "version": "6.0.2", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -13489,6 +13457,7 @@ "version": "14.0.1", "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", + "dev": true, "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", @@ -13508,6 +13477,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, "engines": { "node": ">=12" }, @@ -13519,6 +13489,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, "engines": { "node": ">=14.16" }, @@ -14726,6 +14697,7 @@ "version": "5.16.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "dev": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -14937,7 +14909,8 @@ "node_modules/es-module-lexer": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", - "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==" + "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==", + "dev": true }, "node_modules/es-object-atoms": { "version": "1.0.0", @@ -15565,6 +15538,7 @@ }, "node_modules/eslint-scope": { "version": "5.1.1", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -15871,6 +15845,7 @@ }, "node_modules/esrecurse": { "version": "4.3.0", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -15881,6 +15856,7 @@ }, "node_modules/esrecurse/node_modules/estraverse": { "version": "5.3.0", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -15888,6 +15864,7 @@ }, "node_modules/estraverse": { "version": "4.3.0", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -15939,6 +15916,7 @@ }, "node_modules/events": { "version": "3.3.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.8.x" @@ -16151,6 +16129,7 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", + "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -16170,6 +16149,7 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", + "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -16949,6 +16929,7 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/global": { @@ -17049,7 +17030,8 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", @@ -17639,6 +17621,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, "engines": { "node": ">= 4" } @@ -18837,6 +18820,7 @@ }, "node_modules/jest-worker": { "version": "27.4.5", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -18849,6 +18833,7 @@ }, "node_modules/jest-worker/node_modules/has-flag": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -18856,6 +18841,7 @@ }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -19025,10 +19011,12 @@ }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", + "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { @@ -19238,7 +19226,8 @@ }, "node_modules/keycharm": { "version": "0.4.0", - "license": "(Apache-2.0 OR MIT)" + "license": "(Apache-2.0 OR MIT)", + "peer": true }, "node_modules/keycode": { "version": "2.2.0", @@ -19653,6 +19642,7 @@ }, "node_modules/loader-runner": { "version": "4.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6.11.5" @@ -20094,6 +20084,7 @@ }, "node_modules/merge-stream": { "version": "2.0.0", + "dev": true, "license": "MIT" }, "node_modules/merge2": { @@ -20156,6 +20147,7 @@ }, "node_modules/mime-db": { "version": "1.51.0", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -20163,6 +20155,7 @@ }, "node_modules/mime-types": { "version": "2.1.34", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.51.0" @@ -20377,6 +20370,7 @@ "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true, "engines": { "node": "*" } @@ -20598,6 +20592,7 @@ }, "node_modules/neo-async": { "version": "2.6.2", + "dev": true, "license": "MIT" }, "node_modules/ng2-charts": { @@ -22913,6 +22908,7 @@ }, "node_modules/punycode": { "version": "2.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -22978,6 +22974,7 @@ }, "node_modules/randombytes": { "version": "2.1.0", + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" @@ -23392,6 +23389,7 @@ }, "node_modules/require-from-string": { "version": "2.0.2", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -23734,6 +23732,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -23862,6 +23861,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, "dependencies": { "randombytes": "^2.1.0" } @@ -24074,15 +24074,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/simplemde": { - "version": "1.11.2", - "license": "MIT", - "dependencies": { - "codemirror": "*", - "codemirror-spell-checker": "*", - "marked": "*" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -24289,6 +24280,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -24336,6 +24328,7 @@ }, "node_modules/source-map-support": { "version": "0.5.21", + "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -25121,6 +25114,7 @@ }, "node_modules/tapable": { "version": "2.2.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -25275,6 +25269,7 @@ "version": "5.31.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", + "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -25292,6 +25287,7 @@ "version": "5.3.10", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", @@ -25323,6 +25319,7 @@ }, "node_modules/terser-webpack-plugin/node_modules/ajv": { "version": "6.12.6", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -25337,6 +25334,7 @@ }, "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { "version": "3.5.2", + "dev": true, "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" @@ -25344,10 +25342,12 @@ }, "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { "version": "0.4.1", + "dev": true, "license": "MIT" }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { "version": "3.1.1", + "dev": true, "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", @@ -25380,6 +25380,10 @@ "dev": true, "license": "MIT" }, + "node_modules/textarea-caret": { + "version": "3.1.0", + "resolved": "git+ssh://git@github.com/component/textarea-caret-position.git#b5845a4c39cf094b56925183c086f92c8f8fec68" + }, "node_modules/thingies": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", @@ -25936,10 +25940,6 @@ "node": ">=14.17" } }, - "node_modules/typo-js": { - "version": "1.2.1", - "license": "BSD-3-Clause" - }, "node_modules/ua-parser-js": { "version": "0.7.31", "dev": true, @@ -26008,7 +26008,8 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -26074,6 +26075,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, "engines": { "node": ">=18" }, @@ -26245,6 +26247,7 @@ }, "node_modules/uri-js": { "version": "4.4.1", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -26990,6 +26993,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -27031,6 +27035,7 @@ "version": "5.91.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -27384,6 +27389,7 @@ }, "node_modules/webpack-sources": { "version": "3.2.3", + "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" @@ -27420,6 +27426,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -27435,6 +27442,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -27442,12 +27450,14 @@ "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", diff --git a/frontend/package.json b/frontend/package.json index 8481e96ced..831f431e84 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,13 +31,9 @@ "@graphiql/toolkit": "^0.9.1", "@iharbeck/ngx-virtual-scroller": "^17.0.2", "@lithiumjs/angular": "^8.0.0", - "@lithiumjs/ngx-virtual-scroll": "^0.3.2", - "@marker.io/browser": "^0.19.0", "ace-builds": "^1.34.2", "angular-gridster2": "18.0.1", - "angular-mentions": "1.5.0", "bootstrap": "5.2.3", - "copy-webpack-plugin": "^12.0.2", "core-js": "3.37.1", "cropperjs": "2.0.0-alpha.1", "date-fns": "3.6.0", @@ -45,12 +41,10 @@ "graphql": "16.8.1", "graphql-ws": "^5.16.0", "image-focus": "1.2.1", - "keycharm": "0.4.0", "leaflet": "1.9.4", "leaflet-control-geocoder": "2.4.0", "marked": "12.0.2", "mersenne-twister": "1.1.0", - "moment": "^2.30.1", "mousetrap": "1.6.5", "ng2-charts": "^6.0.1", "ngx-color-picker": "16.0.0", @@ -62,8 +56,8 @@ "react": "18.3.1", "react-dom": "18.3.1", "rxjs": "7.8.1", - "simplemde": "1.11.2", "slugify": "1.6.6", + "textarea-caret": "github:component/textarea-caret-position", "tslib": "2.6.2", "tui-calendar": "^1.15.3", "typemoq": "^2.1.0", @@ -104,12 +98,10 @@ "@types/node": "20.12.13", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@types/simplemde": "1.11.11", - "@types/tapable": "2.2.7", "@types/ws": "8.5.10", "@typescript-eslint/eslint-plugin": "^7.11.0", "@typescript-eslint/parser": "^7.11.0", - "@webcomponents/custom-elements": "^1.6.0", + "copy-webpack-plugin": "^12.0.2", "eslint": "^8.57.0", "eslint-config-airbnb-typescript": "18.0.0", "eslint-plugin-deprecation": "^2.0.0", diff --git a/frontend/src/app/declarations.d.ts b/frontend/src/app/declarations.d.ts index 06f8f6a00e..4fdb5a1081 100644 --- a/frontend/src/app/declarations.d.ts +++ b/frontend/src/app/declarations.d.ts @@ -8,6 +8,11 @@ declare module 'pikaday/pikaday'; declare module 'progressbar.js'; +declare module 'textarea-caret' { + function getCaretCoordinates(element: any, position: number | null): { top: number; left: number }; + export default getCaretCoordinates; +} + declare class SquidexEditorWrapper { constructor(element: HTMLElement, props: EditorProps); diff --git a/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.ts b/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.ts index e12dd270f4..768db9f1ca 100644 --- a/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.ts +++ b/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.ts @@ -38,6 +38,7 @@ export class UsersDataSource implements AutocompleteSource { results.push(user); } } + return results; })); } diff --git a/frontend/src/app/framework/angular/autosize.directive.ts b/frontend/src/app/framework/angular/autosize.directive.ts new file mode 100644 index 0000000000..adcb358735 --- /dev/null +++ b/frontend/src/app/framework/angular/autosize.directive.ts @@ -0,0 +1,51 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + + +import { AfterViewInit, Directive, DoCheck, ElementRef, HostBinding, HostListener, Input, numberAttribute, Renderer2 } from '@angular/core'; + +@Directive({ + standalone: true, + selector: 'textarea[sqxAutosize]', +}) +export class AutosizeDirective implements AfterViewInit, DoCheck { + @HostBinding('style.overflow') + public overflow = 'hidden'; + + @Input({ transform: numberAttribute }) + @HostBinding('rows') + public rows = 1; + + constructor( + private readonly element: ElementRef, + private readonly renderer: Renderer2, + ) { + } + + public ngAfterViewInit() { + this.resize(); + } + + public ngDoCheck() { + this.resize(); + } + + @HostListener('input') + private resize() { + const textarea = this.element.nativeElement as HTMLTextAreaElement; + + // Calculate border height which is not included in scrollHeight + const borderHeight = textarea.offsetHeight - textarea.clientHeight; + + this.setHeight(textarea, 'auto'); + this.setHeight(textarea, `${textarea.scrollHeight + borderHeight}px`); + } + + private setHeight(element: HTMLTextAreaElement, value: string) { + this.renderer.setStyle(element, 'height', value); + } +} \ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html index 4857b6073b..d065faeda0 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html @@ -1,20 +1,47 @@
- +
+   +
+ + @if (textArea) { + + } @else { + + } @if (icon) {
@@ -36,11 +63,11 @@ class="control-dropdown" #container adjustHeight="false" - [adjustWidth]="dropdownFullWidth" + [adjustWidth]="dropdownFullWidth && !startCharacter" [position]="dropdownPosition" scrollX="false" scrollY="true" - [sqxAnchoredTo]="input" + [sqxAnchoredTo]="anchor" *sqxModal="suggestionsModal" [style]="dropdownStyles"> @for (item of snapshot.suggestedItems; track item; let i = $index) { diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.scss b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.scss index 3bc04fb8f7..c30192cd56 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.scss +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.scss @@ -5,6 +5,25 @@ position: relative; } +.anchor { + @include absolute(0, null, null, 0); + background-color: red !important; + z-index: 1000; + opacity: .1; + + & * { + background-color: red !important; + } + + &.full-size { + @include absolute(0, 0, 0, 0); + } + + &.cursor-size { + margin-top: -.375rem !important; + } +} + .form-icon { padding-left: 2.25rem; } @@ -24,4 +43,10 @@ font-weight: normal; padding-left: 5px; padding-right: 5px; +} + +textarea { + overflow-x: hidden; + overflow-y: hidden; + resize: none; } \ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts index 316c0171e0..7aa23046f7 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts @@ -5,12 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ - -import { booleanAttribute, ChangeDetectionStrategy, Component, ContentChild, ElementRef, forwardRef, Input, numberAttribute, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { booleanAttribute, ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, forwardRef, Input, numberAttribute, OnDestroy, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { merge, Observable, of, Subject } from 'rxjs'; import { catchError, debounceTime, finalize, map, switchMap, tap } from 'rxjs/operators'; +import getCaretCoordinates from 'textarea-caret'; import { FloatingPlacement, Keys, ModalModel, StatefulControlComponent, Subscriptions, Types } from '@app/framework/internal'; +import { AutosizeDirective } from '../../autosize.directive'; import { DropdownMenuComponent } from '../../dropdown-menu.component'; import { LoaderComponent } from '../../loader.component'; import { ModalPlacementDirective } from '../../modals/modal-placement.directive'; @@ -24,10 +25,24 @@ export interface AutocompleteSource { find(query: string): Observable>; } +export const NOOP_DATASOURCE = { + find() { + return of([]); + }, +}; + export const SQX_AUTOCOMPLETE_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AutocompleteComponent), multi: true, }; +interface Query { + // The query text. + text: string; + + // The range. + range?: { from: number; to: number }; +} + interface State { // The suggested items. suggestedItems: ReadonlyArray; @@ -38,11 +53,16 @@ interface State { // True, when the searching is in progress. isSearching?: boolean; + // The last query. + lastQuery?: Query; + // Indicates whether the loading is in progress. isLoading?: boolean; } const NO_EMIT = { emitEvent: false }; +const NO_QUERY = { text: '' }; +const RANGE_LIMIT = 60; @Component({ standalone: true, @@ -54,6 +74,7 @@ const NO_EMIT = { emitEvent: false }; ], changeDetection: ChangeDetectionStrategy.OnPush, imports: [ + AutosizeDirective, DropdownMenuComponent, FocusOnInitDirective, FormsModule, @@ -68,9 +89,24 @@ const NO_EMIT = { emitEvent: false }; }) export class AutocompleteComponent extends StatefulControlComponent> implements OnInit, OnDestroy { private readonly subscriptions = new Subscriptions(); - private readonly modalStream = new Subject(); + private readonly modalStream = new Subject(); + private lastCursor = 0; private timer: any; + @Output() + public editorBlur = new EventEmitter(); + + @Output() + public editorKeyDown = new EventEmitter(); + + @Output() + public editorKeyPress = new EventEmitter(); + + @Input({ transform: booleanAttribute }) + public set disabled(value: boolean | undefined | null) { + this.setDisabledState(value === true); + } + @Input({ required: true }) public itemsSource!: AutocompleteSource; @@ -86,6 +122,9 @@ export class AutocompleteComponent extends StatefulControlComponent; + @ViewChild('anchor', { static: false }) + public anchor!: ElementRef; + @ViewChild('input', { static: false }) public inputControl!: ElementRef; @@ -148,29 +191,55 @@ export class AutocompleteComponent extends StatefulControlComponent = this.queryInput.valueChanges.pipe( tap(query => { this.callChange(query); + this.next({ lastQuery: undefined }); }), - map(query => { - if (Types.isString(query)) { - return query.trim(); - } else { - return ''; + map((text: string) => { + if (!Types.isString(text)) { + return NO_QUERY; + } + + if (!this.startCharacter) { + return { text: text.trim() }; } + + let rangeTo = Math.min(this.lastCursor, text.length); + let rangeFrom = -1; + for (let j = rangeTo - 1; j >= Math.max(0, rangeTo - RANGE_LIMIT); j--) { + const char = text[j]; + if (char === this.startCharacter) { + rangeFrom = j; + } + + if (/[\s]/.test(char)) { + break; + } + } + + if (rangeFrom >= 0 && rangeFrom < rangeTo) { + text = text.substring(rangeFrom + 1, rangeTo + 1); + + return { text, range: { from: rangeFrom, to: rangeTo } }; + } + + return { text: '' }; }), debounceTime(this.debounceTime)); this.subscriptions.add( - merge(inputStream, this.modalStream).pipe( + merge(queryStream, this.modalStream).pipe( switchMap(query => { - if (!this.itemsSource) { + this.next({ lastQuery: query }); + + if (!this.itemsSource || query.text === '') { return of([]); } else { this.setLoading(true); - return this.itemsSource.find(query).pipe( + return this.itemsSource.find(query.text ).pipe( finalize(() => { this.setLoading(false); }), @@ -179,6 +248,7 @@ export class AutocompleteComponent extends StatefulControlComponent { + this.updateAnchor(); this.next({ suggestedIndex: -1, suggestedItems: items || [], @@ -188,17 +258,26 @@ export class AutocompleteComponent extends StatefulControlComponent 0 && this.selectItem()); + return !this.selectItem(); } return true; @@ -227,25 +306,23 @@ export class AutocompleteComponent extends StatefulControlComponent 0) { - this.queryInput.setValue(selection[this.displayProperty], NO_EMIT); + displayString = selection[this.displayProperty]; } else { - this.queryInput.setValue(selection.toString(), NO_EMIT); + displayString = selection.toString(); } - let value = selection; + const query = this.snapshot.lastQuery; + if (query?.range) { + const input = this.queryInput.value; + const textBefore = input.substring(0, query.range.from); + const textAfter = input.substring(query.range.to + 1); + + displayString = `${textBefore}${this.startCharacter}${displayString}${textAfter}`; + } + + this.queryInput.setValue(displayString, NO_EMIT); + let value = selection; if (this.valueProperty) { value = selection[this.valueProperty]; } @@ -318,4 +406,32 @@ export class AutocompleteComponent extends StatefulControlComponent inputWidth) { + x = inputWidth - w; + } + + x = Math.max(0, x); + w = Math.min(w, inputWidth); + + this.anchor.nativeElement.style.top = `${coordsStart.top - 2}px`; + this.anchor.nativeElement.style.left = `${x}px`; + this.anchor.nativeElement.style.width = `${w}px`; + } } diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts b/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts index 0c8cb9276c..5fc1ef5c8a 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts @@ -5,6 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; import { map, Observable, timer } from 'rxjs'; import { AutocompleteComponent, AutocompleteSource, LocalizerService, RootViewComponent } from '@app/framework'; @@ -29,18 +30,25 @@ export default { props: args, template: ` - - +
+ + +
`, }), decorators: [ moduleMetadata({ imports: [ + BrowserAnimationsModule, RootViewComponent, ], providers: [ @@ -61,7 +69,7 @@ class Source implements AutocompleteSource { } public find(query: string): Observable { - return timer(this.delay).pipe(map(() => this.values.filter(x => x.indexOf(query) >= 0))); + return timer(this.delay).pipe(map(() => this.values.filter(x => query.length > 0 && x.indexOf(query) >= 0))); } } @@ -98,9 +106,33 @@ export const StyleUnderlined: Story = { }, }; +export const StyleFullWidth: Story = { + args: { + itemsSource: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing']), + dropdownFullWidth: true, + }, +}; + export const IconLoading: Story = { args: { - itemsSource: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing'], 4000), + itemsSource: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing'], 2000), icon: 'user', }, +}; + +export const StartCharacter: Story = { + args: { + debounceTime: 0, + itemsSource: new Source(['donald@duck.com', 'scrooge@mcduck.com']), + startCharacter: '@', + }, +}; + +export const TextArea: Story = { + args: { + debounceTime: 0, + itemsSource: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing']), + startCharacter: '@', + textArea: true, + }, }; \ No newline at end of file diff --git a/frontend/src/app/framework/utils/markdown.ts b/frontend/src/app/framework/utils/markdown.ts index d51bd64b22..84f94acb91 100644 --- a/frontend/src/app/framework/utils/markdown.ts +++ b/frontend/src/app/framework/utils/markdown.ts @@ -55,7 +55,7 @@ export function markdownRender(input: string | undefined | null, inline: boolean if (inline) { return marked(input, { renderer: RENDERER_INLINE }) as string; } else { - return marked(input, { renderer: RENDERER_DEFAULT }) as string; + return marked(input, { renderer: RENDERER_DEFAULT, breaks: true }) as string; } } diff --git a/frontend/src/app/shared/components/comments/comment.component.html b/frontend/src/app/shared/components/comments/comment.component.html index cf1b3f5749..bfb045dcb0 100644 --- a/frontend/src/app/shared/components/comments/comment.component.html +++ b/frontend/src/app/shared/components/comments/comment.component.html @@ -64,24 +64,22 @@ } @if (snapshot.mode === "Edit") { -
- -
+ + + +
-
@@ -96,29 +94,26 @@ canFollow="false" [commentItem]="item" [comments]="comments" - [mentionConfig]="mentionConfig" - [mentionUsers]="mentionUsers" [userToken]="userToken"> } @if (snapshot.mode === "Reply") { - -
+ sqxFocusOnInit + startCharacter="@" + textArea="true"> + +
-
diff --git a/frontend/src/app/shared/components/comments/comment.component.ts b/frontend/src/app/shared/components/comments/comment.component.ts index 1a57230629..d850009b52 100644 --- a/frontend/src/app/shared/components/comments/comment.component.ts +++ b/frontend/src/app/shared/components/comments/comment.component.ts @@ -7,11 +7,10 @@ import { booleanAttribute, ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; import { RouterLink } from '@angular/router'; -import { MentionConfig, MentionModule } from 'angular-mentions'; -import { bounceAnimation, ConfirmClickDirective, FocusOnInitDirective, FromNowPipe, MarkdownPipe, SafeHtmlPipe, ScrollActiveDirective, TooltipDirective, TranslatePipe } from '@app/framework'; -import { CommentItem, CommentsState, ContributorDto, DialogService, Keys, StatefulComponent, UpsertCommentForm } from '@app/shared/internal'; +import { AutocompleteComponent, AutocompleteSource, bounceAnimation, ConfirmClickDirective, FromNowPipe, MarkdownPipe, NOOP_DATASOURCE, SafeHtmlPipe, ScrollActiveDirective, TooltipDirective, TranslatePipe } from '@app/framework'; +import { CommentItem, CommentsState, DialogService, Keys, StatefulComponent, UpsertCommentForm } from '@app/shared/internal'; import { UserNameRefPipe, UserPictureRefPipe } from '../pipes'; interface State { @@ -28,12 +27,10 @@ interface State { bounceAnimation, ], imports: [ + AutocompleteComponent, ConfirmClickDirective, - FocusOnInitDirective, - FormsModule, FromNowPipe, MarkdownPipe, - MentionModule, ReactiveFormsModule, RouterLink, SafeHtmlPipe, @@ -66,28 +63,25 @@ export class CommentComponent extends StatefulComponent { @Input({ required: true }) public comments!: CommentsState; + @Input() + public contributors: AutocompleteSource = NOOP_DATASOURCE; + @Input() public userToken = ''; @Input() public currentUrl = ''; - @Input({ required: true }) - public mentionUsers?: ReadonlyArray; - - @Input({ required: true }) - public mentionConfig!: MentionConfig; - @Input() public scrollContainer?: string; public replyForm = new UpsertCommentForm(); + public updateForm = new UpsertCommentForm(); + public isDeletable = false; public isEditable = false; - public editingText = ''; - constructor( private readonly dialogs: DialogService, ) { @@ -102,8 +96,7 @@ export class CommentComponent extends StatefulComponent { } public startEdit() { - this.editingText = this.commentItem.comment.text; - + this.updateForm.load({ text: this.commentItem.comment.text }); this.next({ mode: 'Edit' }); } @@ -111,7 +104,7 @@ export class CommentComponent extends StatefulComponent { this.next({ mode: 'Reply' }); } - public cancelEditOrReply() { + public cancelUpdateOrReply() { this.next({ mode: 'Normal' }); } @@ -137,7 +130,7 @@ export class CommentComponent extends StatefulComponent { } this.replyForm.submitCompleted(); - this.cancelEditOrReply(); + this.cancelUpdateOrReply(); } public update() { @@ -145,7 +138,7 @@ export class CommentComponent extends StatefulComponent { return; } - const text = this.editingText; + const { text } = this.updateForm.submit() || {}; if (!text || text.length === 0) { this.dialogs.confirm('i18n:comments.deleteConfirmTitle', 'i18n:comments.deleteConfirmText') @@ -158,20 +151,21 @@ export class CommentComponent extends StatefulComponent { this.comments.update(this.commentItem.index, { text }); } - this.cancelEditOrReply(); + this.updateForm.submitCompleted(); + this.cancelUpdateOrReply(); } - public replayOnEnter(event: KeyboardEvent) { + public onKeyPressReply(event: KeyboardEvent) { if (Keys.isEnter(event) && !event.altKey && !event.shiftKey) { - event.preventDefault(); this.reply(); + event.preventDefault(); } } - public updateOnEnter(event: KeyboardEvent) { + public onKeyPressUpdate(event: KeyboardEvent) { if (Keys.isEnter(event) && !event.altKey && !event.shiftKey) { - event.preventDefault(); this.update(); + event.preventDefault(); } } } diff --git a/frontend/src/app/shared/components/comments/comments.component.html b/frontend/src/app/shared/components/comments/comments.component.html index e741766ece..c71cda33a5 100644 --- a/frontend/src/app/shared/components/comments/comments.component.html +++ b/frontend/src/app/shared/components/comments/comments.component.html @@ -1,33 +1,37 @@ -@if (mentionUsers | async; as users) { -
- - - -
-
- @for (comment of commentsItems | async; track comment.index) { - - } -
-} +
+
+ + + + + + {{ contributor.contributorName }} + + + +
+
+
+ @for (comment of commentsItems | async; track comment.index) { + + } +
diff --git a/frontend/src/app/shared/components/comments/comments.component.scss b/frontend/src/app/shared/components/comments/comments.component.scss index f72b1d7f40..c6065871dd 100644 --- a/frontend/src/app/shared/components/comments/comments.component.scss +++ b/frontend/src/app/shared/components/comments/comments.component.scss @@ -10,6 +10,25 @@ .mention-active > a { background: $color-theme-brand !important; } + + .comments-header { + .form-control { + border-color: $color-white; + border-bottom-color: $color-border; + border-radius: 0; + + &:focus { + box-shadow: none; + border-color: $color-theme-brand; + } + } + } +} + +.autocomplete-user { + .user-name { + margin-left: .25rem; + } } .comments { @@ -24,15 +43,4 @@ &-header { flex-shrink: 0; } -} - -.form-control { - border-color: $color-white; - border-bottom-color: $color-border; - border-radius: 0; - - &:focus { - box-shadow: none; - border-color: $color-theme-brand; - } } \ No newline at end of file diff --git a/frontend/src/app/shared/components/comments/comments.component.ts b/frontend/src/app/shared/components/comments/comments.component.ts index e2a19c84ea..8f460ba7d2 100644 --- a/frontend/src/app/shared/components/comments/comments.component.ts +++ b/frontend/src/app/shared/components/comments/comments.component.ts @@ -6,27 +6,32 @@ */ import { AsyncPipe } from '@angular/common'; -import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Router } from '@angular/router'; -import { MentionConfig, MentionModule } from 'angular-mentions'; import { BehaviorSubject } from 'rxjs'; -import { MessageBus, Subscriptions, TranslatePipe } from '@app/framework'; -import { AnnotationCreateAfterNavigate, AnnotationsSelectAfterNavigate, AuthService, CommentsState, ContributorsState, UpsertCommentForm } from '@app/shared/internal'; +import { AutocompleteComponent, Keys, MessageBus, Subscriptions, TranslatePipe } from '@app/framework'; +import { AnnotationCreateAfterNavigate, AnnotationsSelectAfterNavigate, AuthService, CommentsState, UpsertCommentForm } from '@app/shared/internal'; +import { UserPicturePipe } from '../pipes'; import { CommentComponent } from './comment.component'; +import { ContributorsDataSource } from './data-source'; @Component({ standalone: true, selector: 'sqx-comments', styleUrls: ['./comments.component.scss'], templateUrl: './comments.component.html', + providers: [ + ContributorsDataSource, + ], imports: [ AsyncPipe, + AutocompleteComponent, CommentComponent, FormsModule, - MentionModule, ReactiveFormsModule, TranslatePipe, + UserPicturePipe, ], }) export class CommentsComponent implements OnInit { @@ -35,14 +40,11 @@ export class CommentsComponent implements OnInit { private reference?: AnnotationCreateAfterNavigate; @ViewChild('input', { static: false }) - public input!: ElementRef; + public input!: AutocompleteComponent; @Input() public commentsId = ''; - public mentionUsers = this.contributorsState.contributors; - public mentionConfig: MentionConfig = { dropUp: false, labelKey: 'contributorEmail' }; - public commentForm = new UpsertCommentForm(); public commentsItems = this.commentsState.getGroupedComments(this.selection); public commentUser: string; @@ -50,14 +52,14 @@ export class CommentsComponent implements OnInit { constructor(authService: AuthService, public readonly commentsState: CommentsState, public readonly router: Router, - private readonly contributorsState: ContributorsState, + public readonly contributorsDataSource: ContributorsDataSource, private readonly messageBus: MessageBus, ) { this.commentUser = authService.user!.token; } public ngOnInit() { - this.contributorsState.loadIfNotLoaded(); + this.contributorsDataSource.loadIfNotLoaded(); this.subscriptions.add( this.messageBus.of(AnnotationsSelectAfterNavigate) @@ -70,7 +72,7 @@ export class CommentsComponent implements OnInit { .subscribe(message => { this.reference = message; - this.input.nativeElement.focus(); + this.input.focus(); })); } @@ -86,9 +88,16 @@ export class CommentsComponent implements OnInit { this.commentForm.submitCompleted(); } - public blurComment() { + public onKeyPress(event: KeyboardEvent) { + if (Keys.isEnter(event) && !event.altKey && !event.shiftKey) { + this.comment(); + event.preventDefault(); + } + } + + public onBlur() { setTimeout(() => { this.reference = undefined; }, 100); } -} +} \ No newline at end of file diff --git a/frontend/src/app/shared/components/comments/data-source.ts b/frontend/src/app/shared/components/comments/data-source.ts new file mode 100644 index 0000000000..97894a352d --- /dev/null +++ b/frontend/src/app/shared/components/comments/data-source.ts @@ -0,0 +1,33 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Injectable } from '@angular/core'; +import { map, Observable, of } from 'rxjs'; +import { AutocompleteSource } from '@app/framework'; +import { ContributorsState } from '@app/shared/internal'; + +@Injectable() +export class ContributorsDataSource implements AutocompleteSource { + constructor( + private readonly contributorsState: ContributorsState, + ) { + } + + public loadIfNotLoaded() { + this.contributorsState.loadIfNotLoaded(); + } + + public find(query: string): Observable> { + if (!query) { + return of([]); + } + + return this.contributorsState.contributors.pipe( + map(contributors => contributors.filter(c => c.contributorEmail.indexOf(query) >= 0)), + ); + } +} \ No newline at end of file diff --git a/frontend/src/app/shell/index.ts b/frontend/src/app/shell/index.ts index 2536639fb6..9bb1d5f95f 100644 --- a/frontend/src/app/shell/index.ts +++ b/frontend/src/app/shell/index.ts @@ -11,7 +11,6 @@ export * from './pages/forbidden/forbidden-page.component'; export * from './pages/home/home-page.component'; export * from './pages/internal/apps-menu.component'; export * from './pages/internal/internal-area.component'; -export * from './pages/internal/feedback-menu.component'; export * from './pages/internal/logo.component'; export * from './pages/internal/notification-dropdown.component'; export * from './pages/internal/notifications-menu.component'; diff --git a/frontend/src/app/shell/pages/internal/feedback-menu.component.html b/frontend/src/app/shell/pages/internal/feedback-menu.component.html deleted file mode 100644 index adde3075fc..0000000000 --- a/frontend/src/app/shell/pages/internal/feedback-menu.component.html +++ /dev/null @@ -1,7 +0,0 @@ -@if (markerProject) { - -} diff --git a/frontend/src/app/shell/pages/internal/feedback-menu.component.scss b/frontend/src/app/shell/pages/internal/feedback-menu.component.scss deleted file mode 100644 index 2742d895e7..0000000000 --- a/frontend/src/app/shell/pages/internal/feedback-menu.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'mixins'; -@import 'vars'; \ No newline at end of file diff --git a/frontend/src/app/shell/pages/internal/feedback-menu.component.ts b/frontend/src/app/shell/pages/internal/feedback-menu.component.ts deleted file mode 100644 index 33a1066cbc..0000000000 --- a/frontend/src/app/shell/pages/internal/feedback-menu.component.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - - -import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit } from '@angular/core'; -import markerSDK, { MarkerSdk } from '@marker.io/browser'; -import { UIOptions } from '@app/shared'; - -@Component({ - standalone: true, - selector: 'sqx-feedback-menu', - styleUrls: ['./feedback-menu.component.scss'], - templateUrl: './feedback-menu.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class FeedbackMenuComponent implements OnInit, OnDestroy { - private widget?: MarkerSdk; - - public readonly markerProject = inject(UIOptions).value.markerProject; - - public ngOnDestroy() { - this.widget?.unload(); - } - - public async ngOnInit() { - if (!this.markerProject) { - return; - } - - this.widget = await markerSDK.loadWidget({ project: this.markerProject }); - this.widget.hide(); - } - - public capture() { - this.widget?.capture('fullscreen'); - } -} diff --git a/frontend/src/app/shell/pages/internal/internal-area.component.html b/frontend/src/app/shell/pages/internal/internal-area.component.html index e9ce5450d1..f98399edef 100644 --- a/frontend/src/app/shell/pages/internal/internal-area.component.html +++ b/frontend/src/app/shell/pages/internal/internal-area.component.html @@ -10,7 +10,6 @@ -
diff --git a/frontend/src/app/shell/pages/internal/internal-area.component.ts b/frontend/src/app/shell/pages/internal/internal-area.component.ts index facd55ea86..c874ee92c4 100644 --- a/frontend/src/app/shell/pages/internal/internal-area.component.ts +++ b/frontend/src/app/shell/pages/internal/internal-area.component.ts @@ -12,7 +12,6 @@ import { DialogService, LoadingService, Notification, Subscriptions, UIOptions } import { AssetUploaderComponent } from '@app/shared/components/assets/asset-uploader.component'; import { AppsMenuComponent } from './apps-menu.component'; import { ChatMenuComponent } from './chat-menu.component'; -import { FeedbackMenuComponent } from './feedback-menu.component'; import { LogoComponent } from './logo.component'; import { NotificationsMenuComponent } from './notifications-menu.component'; import { ProfileMenuComponent } from './profile-menu.component'; @@ -28,7 +27,6 @@ import { SearchMenuComponent } from './search-menu.component'; AssetUploaderComponent, AsyncPipe, ChatMenuComponent, - FeedbackMenuComponent, LogoComponent, NotificationsMenuComponent, ProfileMenuComponent, diff --git a/frontend/src/app/shell/pages/internal/notification-dropdown.component.html b/frontend/src/app/shell/pages/internal/notification-dropdown.component.html index 15d1674628..a58b890b6f 100644 --- a/frontend/src/app/shell/pages/internal/notification-dropdown.component.html +++ b/frontend/src/app/shell/pages/internal/notification-dropdown.component.html @@ -24,8 +24,6 @@ [commentItem]="item" [comments]="commentsState" confirmDelete="false" - [mentionConfig]="{}" - [mentionUsers]="undefined" [userToken]="commentUser"> } @empty {