From 63778b8db5571f9602108ef0d44b32b378b71cc3 Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Sun, 13 Oct 2024 15:51:59 -0500 Subject: [PATCH] [web_app] Minor cleanup and consistency improvements --- pkg/web_app/lib/src/_dom_helper.dart | 32 +++++++-------- pkg/web_app/lib/src/account.dart | 11 +++--- pkg/web_app/lib/src/admin_pages.dart | 17 ++++---- pkg/web_app/lib/src/deferred/markdown.dart | 2 +- pkg/web_app/lib/src/foldable.dart | 4 +- pkg/web_app/lib/src/gtm_js.dart | 6 +-- pkg/web_app/lib/src/hoverable.dart | 19 ++++----- pkg/web_app/lib/src/issues.dart | 5 ++- pkg/web_app/lib/src/likes.dart | 9 +++-- pkg/web_app/lib/src/mobile_nav.dart | 2 +- pkg/web_app/lib/src/page_data.dart | 6 +-- pkg/web_app/lib/src/page_updater.dart | 11 +++--- pkg/web_app/lib/src/screenshot_carousel.dart | 39 ++++++++++--------- pkg/web_app/lib/src/scroll.dart | 10 ++--- pkg/web_app/lib/src/search.dart | 28 ++++++------- pkg/web_app/lib/src/web_util.dart | 4 ++ .../lib/src/widget/completion/widget.dart | 10 ++--- pkg/web_app/lib/src/widget/switch/widget.dart | 4 +- pkg/web_app/pubspec.lock | 2 +- pkg/web_app/pubspec.yaml | 1 + pkg/web_app/test/inner_html_test.dart | 2 +- 21 files changed, 116 insertions(+), 108 deletions(-) diff --git a/pkg/web_app/lib/src/_dom_helper.dart b/pkg/web_app/lib/src/_dom_helper.dart index 2291909d21..125d674c8f 100644 --- a/pkg/web_app/lib/src/_dom_helper.dart +++ b/pkg/web_app/lib/src/_dom_helper.dart @@ -9,7 +9,7 @@ import 'package:mdc_web/mdc_web.dart' show MDCDialog; import 'deferred/markdown.dart' deferred as md; /// Displays a message via the modal window. -Future modalMessage(String title, Element content) async { +Future modalMessage(String title, Element content) async { await modalWindow( titleText: title, content: content, @@ -85,9 +85,9 @@ Element _buildDialog({ /// The callback will be called with `true` when "OK" was clicked, and `false` /// when "Cancel" was clicked. - required Function(bool) closing, + required void Function(bool) closing, }) => - Element.div() + DivElement() ..classes.add('mdc-dialog') ..attributes.addAll({ 'role': 'alertdialog', @@ -96,17 +96,17 @@ Element _buildDialog({ 'aria-describedby': 'pub-dialog-content', }) ..children = [ - Element.div() + DivElement() ..classes.add('mdc-dialog__container') ..children = [ - Element.div() + DivElement() ..classes.add('mdc-dialog__surface') ..children = [ - Element.tag('h2') + HeadingElement.h2() ..classes.add('mdc-dialog__title') ..id = 'pub-dialog-title' ..innerText = titleText, - Element.div() + DivElement() ..classes.add('mdc-dialog__content') ..id = 'pub-dialog-content' ..children = [content], @@ -114,7 +114,7 @@ Element _buildDialog({ ..classes.add('mdc-dialog__actions') ..children = [ if (isQuestion) - Element.tag('button') + ButtonElement() ..classes.addAll([ 'mdc-button', 'mdc-dialog__button', @@ -126,11 +126,11 @@ Element _buildDialog({ closing(false); }) ..children = [ - Element.span() + SpanElement() ..classes.add('mdc-button__label') ..innerText = cancelButtonText ?? 'Cancel', ], - Element.tag('button') + ButtonElement() ..classes.addAll([ 'mdc-button', 'mdc-dialog__button', @@ -142,18 +142,18 @@ Element _buildDialog({ closing(true); }) ..children = [ - Element.span() + SpanElement() ..classes.add('mdc-button__label') ..innerText = okButtonText ?? 'Ok', ], ], ], ], - Element.div()..classes.add('mdc-dialog__scrim'), + DivElement()..classes.add('mdc-dialog__scrim'), ]; /// Creates an [Element] with unformatted [text] content. -Element text(String text) => Element.div()..text = text; +Element text(String text) => DivElement()..text = text; /// Creates an [Element] with Markdown-formatted content. Future markdown(String text) async { @@ -203,12 +203,12 @@ bool _isInsideContent(Element e, Element content) { /// Disables all focusable elements, except for the elements inside /// [allowedComponents]. Returns a [Function] that will restore the /// original focusability state of the disabled elements. -Function disableAllFocusability({ +void Function() disableAllFocusability({ required List allowedComponents, }) { final focusableElements = document.body!.querySelectorAll(_focusableSelectors.join(', ')); - final restoreFocusabilityFns = []; + final restoreFocusabilityFns = []; for (final e in focusableElements) { if (allowedComponents.any((content) => _isInsideContent(e, content))) { continue; @@ -224,7 +224,7 @@ Function disableAllFocusability({ /// Update [e] to disable focusability and return a [Function] that can be /// called to revert its original state. -Function _disableFocusability(Element e) { +void Function() _disableFocusability(Element e) { final isLink = e.tagName.toLowerCase() == 'a'; final hasTabindex = e.hasAttribute('tabindex'); final attributesToSet = { diff --git a/pkg/web_app/lib/src/account.dart b/pkg/web_app/lib/src/account.dart index 7cc6fd4991..943ee2a002 100644 --- a/pkg/web_app/lib/src/account.dart +++ b/pkg/web_app/lib/src/account.dart @@ -5,10 +5,9 @@ import 'dart:async'; import 'dart:html'; -import 'package:web_app/src/page_data.dart'; - import 'admin_pages.dart' deferred as admin_pages; import 'api_client/api_client.dart' deferred as api_client; +import 'page_data.dart'; final _signInButton = document.getElementById('-account-login'); final isNotAuthenticated = _signInButton != null; @@ -29,8 +28,8 @@ void _initSessionMonitor() { return; } - final minCheckDelay = Duration(minutes: 5); - final authenticationThreshold = Duration(minutes: 55); + const minCheckDelay = Duration(minutes: 5); + const authenticationThreshold = Duration(minutes: 55); final maxDurationBetweenChecks = authenticationThreshold - minCheckDelay; final sessionExpiresThreshold = authenticationThreshold - (minCheckDelay * 2); @@ -86,7 +85,7 @@ void _initSessionMonitor() { // unless the user session timed out or expired. // // In any of the above cases, defaulting to the default frequency is safe. - await Future.delayed(minCheckDelay); + await Future.delayed(minCheckDelay); } else { // When the session is active, we can run the next check just before the // authentication threshold maxes out. Added sanity checks to bound the delay @@ -99,7 +98,7 @@ void _initSessionMonitor() { } else if (nextCheck < minCheckDelay) { nextCheck = minCheckDelay; } - await Future.delayed(nextCheck); + await Future.delayed(nextCheck); } // Update session information and label. diff --git a/pkg/web_app/lib/src/admin_pages.dart b/pkg/web_app/lib/src/admin_pages.dart index 269f63b19d..26a64c4edf 100644 --- a/pkg/web_app/lib/src/admin_pages.dart +++ b/pkg/web_app/lib/src/admin_pages.dart @@ -37,7 +37,7 @@ void _initGenericForm() { button.onClick.listen((event) async { final body = {}; for (final field in form.querySelectorAll('[name]')) { - final name = field.attributes['name']!; + final name = field.getAttribute('name')!; if (field is InputElement && !(field.disabled ?? false) && field.value != null) { @@ -54,7 +54,7 @@ void _initGenericForm() { api_client.sendJson(verb: 'POST', path: endpoint, body: body), successMessage: null, onSuccess: (r) async { - final result = r ?? {}; + final result = r ?? {}; // Goto a new location to display the feedback message. if (onSuccessGotoUrl != null) { window.location.href = onSuccessGotoUrl; @@ -125,10 +125,9 @@ class _PkgAdminWidget { .getElementById('-admin-restore-retract-package-version-button'); _restoreRetractPackageVersionButton?.onClick .listen((_) => _restoreRetracted()); - if (_inviteUploaderContent != null) { - _inviteUploaderContent!.remove(); - _inviteUploaderContent!.classes.remove('modal-content-hidden'); - } + _inviteUploaderContent + ?..classes.remove('modal-content-hidden') + ..remove(); for (final btn in document.querySelectorAll('.-pub-remove-uploader-button')) { btn.onClick.listen((_) => _removeUploader(btn.dataset['email']!)); @@ -205,7 +204,7 @@ class _PkgAdminWidget { isQuestion: true, okButtonText: 'Invite', content: _inviteUploaderContent!, - onExecute: () => _doInviteUploader(), + onExecute: _doInviteUploader, ); } @@ -495,7 +494,7 @@ class _PublisherAdminWidget { isQuestion: true, okButtonText: 'Add', content: _addMemberContent!, - onExecute: () => _inviteMember(), + onExecute: _inviteMember, ); } @@ -553,7 +552,7 @@ class _ConsentWidget { void _updateButtons(bool? granted) { final text = granted! ? 'Consent accepted.' : 'Consent rejected.'; - _buttons!.replaceWith(Element.p()..text = text); + _buttons!.replaceWith(ParagraphElement()..text = text); } Future _accept() async { diff --git a/pkg/web_app/lib/src/deferred/markdown.dart b/pkg/web_app/lib/src/deferred/markdown.dart index 6bc8d603c7..0b24e5e0d8 100644 --- a/pkg/web_app/lib/src/deferred/markdown.dart +++ b/pkg/web_app/lib/src/deferred/markdown.dart @@ -8,7 +8,7 @@ import 'package:markdown/markdown.dart' as md; /// Creates an [Element] with Markdown-formatted content. Future markdown(String text) async { - return Element.div() + return DivElement() ..setInnerHtml( md.markdownToHtml(text), validator: NodeValidator(uriPolicy: _UnsafeUriPolicy()), diff --git a/pkg/web_app/lib/src/foldable.dart b/pkg/web_app/lib/src/foldable.dart index 9243bf9ad6..71dabef457 100644 --- a/pkg/web_app/lib/src/foldable.dart +++ b/pkg/web_app/lib/src/foldable.dart @@ -4,7 +4,7 @@ import 'dart:async'; import 'dart:html'; -import 'dart:math' show min, max; +import 'dart:math' show max, min; void setupFoldable() { _setEventForFoldable(); @@ -61,7 +61,7 @@ void _setEventForFoldable() { final foldableIcon = h.querySelector('.foldable-icon'); if (foldableIcon != null) { - foldableIcon.attributes['tabindex'] = '0'; + foldableIcon.setAttribute('tabindex', '0'); } // listen on trigger events diff --git a/pkg/web_app/lib/src/gtm_js.dart b/pkg/web_app/lib/src/gtm_js.dart index 82a7e66384..22c76792f6 100644 --- a/pkg/web_app/lib/src/gtm_js.dart +++ b/pkg/web_app/lib/src/gtm_js.dart @@ -7,12 +7,12 @@ library; import 'dart:js_interop'; -import 'package:web/web.dart' as dart_html; +import 'package:web/web.dart' as web; @JS('dataLayer.push') external void _push(JSAny? data); -void _pushMap(Map data) { +void _pushMap(Map data) { _push(data.jsify()); } @@ -25,7 +25,7 @@ void gtmCustomEvent({ 'event': 'custom-event', // hardcoded, used in GTM Trigger 'customEventCategory': category, 'customEventAction': action, - 'customEventLabel': 'path:${dart_html.window.location.pathname}', + 'customEventLabel': 'path:${web.window.location.pathname}', 'customEventValue': '1', }); } diff --git a/pkg/web_app/lib/src/hoverable.dart b/pkg/web_app/lib/src/hoverable.dart index bd853b3c72..1aa944b51c 100644 --- a/pkg/web_app/lib/src/hoverable.dart +++ b/pkg/web_app/lib/src/hoverable.dart @@ -27,15 +27,15 @@ Element? _activeHover; /// Their `:hover` and `.hover` style must match to have the same effect. void _setEventForHoverable() { document.body!.onClick.listen(deactivateHover); - for (Element h in document.querySelectorAll('.hoverable')) { + for (final h in document.querySelectorAll('.hoverable')) { registerHoverable(h); } } /// Deactivates the active hover (hiding the hovering panel). void deactivateHover(_) { - if (_activeHover != null) { - _activeHover!.classes.remove('hover'); + if (_activeHover case final activeHoverElement?) { + activeHoverElement.classes.remove('hover'); _activeHover = null; } } @@ -46,7 +46,7 @@ void registerHoverable(Element h) { if (h != _activeHover) { deactivateHover(e); _activeHover = h; - _activeHover!.classes.add('hover'); + h.classes.add('hover'); e.stopPropagation(); } }); @@ -77,16 +77,17 @@ void _setEventForPackageTitleCopyToClipboard() { Future _animateCopyFeedback(Element feedback) async { feedback.classes.add('visible'); await window.animationFrame; - await Future.delayed(Duration(milliseconds: 1600)); + await Future.delayed(Duration(milliseconds: 1600)); feedback.classes.add('fadeout'); await window.animationFrame; // NOTE: keep in sync with _variables.scss 0.9s animation with the key // $copy-feedback-transition-opacity-delay - await Future.delayed(Duration(milliseconds: 900)); + await Future.delayed(Duration(milliseconds: 900)); await window.animationFrame; - feedback.classes.remove('visible'); - feedback.classes.remove('fadeout'); + feedback.classes + ..remove('visible') + ..remove('fadeout'); } void _copyToClipboard(String text) { @@ -127,7 +128,7 @@ void _setupCopyAndFeedbackButton({ required Element feedback, required String Function() textFn, }) { - copy.attributes['tabindex'] = '0'; + copy.setAttribute('tabindex', '0'); Future doCopy() async { final text = textFn(); diff --git a/pkg/web_app/lib/src/issues.dart b/pkg/web_app/lib/src/issues.dart index 3fc448c412..6aebdca26f 100644 --- a/pkg/web_app/lib/src/issues.dart +++ b/pkg/web_app/lib/src/issues.dart @@ -13,7 +13,7 @@ void setupIssues() { } void _guardReportIssue() { - for (AnchorElement bugLink in document.querySelectorAll('a.github_issue')) { + for (final bugLink in document.querySelectorAll('a.github_issue')) { bugLink.onClick.listen((event) { if (!window.confirm('This link is for reporting issues for the pub site. ' 'If you would like to report a problem with a package, please visit ' @@ -25,7 +25,8 @@ void _guardReportIssue() { } void _fixIssueLinks() { - for (AnchorElement bugLink in document.querySelectorAll('a.github_issue')) { + for (final bugLink + in document.querySelectorAll('a.github_issue').cast()) { var url = Uri.parse(bugLink.href!); final lines = [ 'URL: ${window.location.href}', diff --git a/pkg/web_app/lib/src/likes.dart b/pkg/web_app/lib/src/likes.dart index 01a4ac04c0..184933b179 100644 --- a/pkg/web_app/lib/src/likes.dart +++ b/pkg/web_app/lib/src/likes.dart @@ -7,9 +7,9 @@ import 'dart:html'; import 'package:_pub_shared/format/number_format.dart'; import 'package:mdc_web/mdc_web.dart' show MDCIconButtonToggle; -import 'package:web_app/src/_dom_helper.dart'; -import 'package:web_app/src/account.dart'; +import '_dom_helper.dart'; +import 'account.dart'; import 'api_client/api_client.dart' deferred as api_client; import 'page_data.dart'; @@ -17,7 +17,8 @@ Future _done = Future.value(); /// Ensure only one task runs at the same time. void _enqueue(Future Function() task) { - _done = _done.then((_) => task(), onError: (e) => print('Action failed: $e')); + _done = _done.then((_) => task(), + onError: (Object? e) => print('Action failed: $e')); } void setupLikesList() { @@ -52,7 +53,7 @@ void setupLikes() { if (likeButton == null) return; final iconButtonToggle = MDCIconButtonToggle(likeButton); - int likesDelta = 0; + var likesDelta = 0; // keep in-sync with app/lib/frontend/templates/views/shared/detail/header.dart String likesString() { diff --git a/pkg/web_app/lib/src/mobile_nav.dart b/pkg/web_app/lib/src/mobile_nav.dart index 838b26ceaf..3b4fa336ab 100644 --- a/pkg/web_app/lib/src/mobile_nav.dart +++ b/pkg/web_app/lib/src/mobile_nav.dart @@ -36,7 +36,7 @@ void _setEventForDetailMetadataToggle() { var isVisible = false; // ignore: cancel_subscriptions - StreamSubscription? stateSubscription; + StreamSubscription? stateSubscription; final currentTitle = document.head?.querySelector('title')?.text?.trim(); final currentUrl = window.location.toString(); document.querySelectorAll('.detail-metadata-toggle').forEach((e) { diff --git a/pkg/web_app/lib/src/page_data.dart b/pkg/web_app/lib/src/page_data.dart index a91ec739b3..8414ca4cba 100644 --- a/pkg/web_app/lib/src/page_data.dart +++ b/pkg/web_app/lib/src/page_data.dart @@ -8,7 +8,7 @@ import 'package:_pub_shared/data/page_data.dart'; /// The server-provided config/data for the current page. /// -/// This is the embedded in +/// This is the `` embedded in /// the `head` section of the HTML page. final PageData pageData = _extractData(); @@ -16,8 +16,8 @@ PageData _extractData() { final meta = document.head?.querySelector('meta[name="pub-page-data"]'); if (meta != null) { try { - final text = meta.attributes['content']!; - final map = pageDataJsonCodec.decode(text) as Map; + final text = meta.getAttribute('content')!; + final map = pageDataJsonCodec.decode(text) as Map; return PageData.fromJson(map); } catch (_) { // ignore exception diff --git a/pkg/web_app/lib/src/page_updater.dart b/pkg/web_app/lib/src/page_updater.dart index dc0874d013..066ca39159 100644 --- a/pkg/web_app/lib/src/page_updater.dart +++ b/pkg/web_app/lib/src/page_updater.dart @@ -6,7 +6,7 @@ import 'dart:html'; import 'package:http/http.dart' deferred as http show get; -typedef PopStateFn = Function(); +typedef PopStateFn = void Function(); PopStateFn? _popStateFn; @@ -27,10 +27,9 @@ void setupPageUpdater(PopStateFn popStateFn) { // handle back button updates window.onPopState.listen((event) { - final state = event.state; - if (state is Map && state['html'] is String) { + if (event.state case {'html': final String htmlState?}) { _update( - state['html'] as String, + htmlState, pushState: false, url: null, ); @@ -64,13 +63,13 @@ Future updateBodyWithHttpGet({ required Uri requestUri, String? navigationUrl, Duration timeout = const Duration(seconds: 4), - bool Function()? preupdateCheck, + bool Function()? preUpdateCheck, }) async { try { await http.loadLibrary(); final page = await http.get(requestUri).timeout(timeout); if (page.statusCode == 200) { - if (preupdateCheck == null || preupdateCheck()) { + if (preUpdateCheck == null || preUpdateCheck()) { _update( page.body, pushState: true, diff --git a/pkg/web_app/lib/src/screenshot_carousel.dart b/pkg/web_app/lib/src/screenshot_carousel.dart index 8dded4b650..d98ee862eb 100644 --- a/pkg/web_app/lib/src/screenshot_carousel.dart +++ b/pkg/web_app/lib/src/screenshot_carousel.dart @@ -5,7 +5,7 @@ import 'dart:convert'; import 'dart:html'; -import 'package:web_app/src/_dom_helper.dart'; +import '_dom_helper.dart'; void setupScreenshotCarousel() { _setEventForScreenshot(); @@ -22,19 +22,22 @@ void _setEventForScreenshot() { final next = document.getElementById('-carousel-next')!; final description = document.getElementById('-screenshot-description') as ParagraphElement; - ImageElement? imageElement = + final existingImageElement = document.getElementById('-carousel-image') as ImageElement?; - if (imageElement == null) { + final ImageElement imageElement; + if (existingImageElement != null) { + imageElement = existingImageElement; + } else { imageElement = ImageElement(); imageElement.id = '-carousel-image'; - imageContainer.children.add(imageElement); + imageContainer.append(imageElement); imageElement.className = 'carousel-image'; } Element? focusedTriggerSourceElement; - Function? restoreFocusabilityFn; - List images = []; - List descriptions = []; + void Function()? restoreFocusabilityFn; + var images = []; + var descriptions = []; void hideElement(Element element) { element.style.display = 'none'; @@ -46,7 +49,7 @@ void _setEventForScreenshot() { void showImage(int index) { hideElement(description); - hideElement(imageElement!); + hideElement(imageElement); imageElement.src = images[index]; description.text = descriptions[index]; @@ -64,12 +67,12 @@ void _setEventForScreenshot() { } imageElement.onLoad.listen((_) { - showElement(imageElement!); + showElement(imageElement); showElement(description); }); } - int screenshotIndex = 0; + var screenshotIndex = 0; for (final thumbnail in thumbnails) { void setup() { restoreFocusabilityFn = disableAllFocusability( @@ -80,8 +83,9 @@ void _setEventForScreenshot() { ); focusedTriggerSourceElement = thumbnail; showElement(carousel); - document.body!.classes.remove('overflow-auto'); - document.body!.classes.add('overflow-hidden'); + document.body!.classes + ..remove('overflow-auto') + ..add('overflow-hidden'); images = thumbnail.dataset['thumbnail']!.split(','); final raw = jsonDecode(thumbnail.dataset['thumbnail-descriptions-json']!); descriptions = (raw as List).cast(); @@ -105,16 +109,15 @@ void _setEventForScreenshot() { hideElement(next); hideElement(prev); hideElement(description); - document.body!.classes.remove('overflow-hidden'); - document.body!.classes.add('overflow-auto'); + document.body!.classes + ..remove('overflow-hidden') + ..add('overflow-auto'); screenshotIndex = 0; images.clear(); descriptions.clear(); focusedTriggerSourceElement?.focus(); focusedTriggerSourceElement = null; - if (restoreFocusabilityFn != null) { - restoreFocusabilityFn!(); - } + restoreFocusabilityFn?.call(); } void gotoPrev() { @@ -162,7 +165,7 @@ void _setEventForScreenshot() { } if (event.key == 'Escape') { - event.stopPropagation; + event.stopPropagation(); closeCarousel(); } if (event.key == 'ArrowLeft') { diff --git a/pkg/web_app/lib/src/scroll.dart b/pkg/web_app/lib/src/scroll.dart index 86b4bbf225..f39e1f5dbe 100644 --- a/pkg/web_app/lib/src/scroll.dart +++ b/pkg/web_app/lib/src/scroll.dart @@ -13,7 +13,7 @@ void setupScroll() { } void _scrollToHash() { - final String hash = window.location.hash; + final hash = window.location.hash; if (hash.isNotEmpty) { final id = hash.startsWith('#') ? hash.substring(1) : hash; final list = @@ -44,11 +44,11 @@ void _setEventForAnchorScroll() { } if (target is AnchorElement && target.getAttribute('href') == target.hash && - target.hash != null && - target.hash!.isNotEmpty) { + (target.hash?.isNotEmpty ?? false)) { final elem = document.querySelector(target.hash!); if (elem != null) { - window.history.pushState({}, document.title, target.hash); + window.history + .pushState({}, document.title, target.hash); e.preventDefault(); _scrollTo(elem); } @@ -56,7 +56,7 @@ void _setEventForAnchorScroll() { }); } -Future _scrollTo(Element elem) async { +Future _scrollTo(Element elem) async { // Chrome could provide inconsistent position data just after the page has // been loaded. The first animation frame makes sure that the rendering is // stabilized and the position data is correct. diff --git a/pkg/web_app/lib/src/search.dart b/pkg/web_app/lib/src/search.dart index 13073a7c60..ef8f11b6b9 100644 --- a/pkg/web_app/lib/src/search.dart +++ b/pkg/web_app/lib/src/search.dart @@ -53,32 +53,32 @@ void adjustQueryTextAfterPageShow() { final q = document.querySelector('input[name="q"]') as InputElement?; if (q == null) return null; final uri = Uri.tryParse(window.location.href); - if (q.value != uri?.queryParameters['q']) { - q.value = uri?.queryParameters['q'] ?? q.value; + final qParameter = uri?.queryParameters['q']; + if (q.value != qParameter) { + q.value = qParameter ?? q.value; } } void _setEventsForSearchForm() { // When a search form checkbox has a linked search label, - //checking the checkbox will trigger a click on the link. + // checking the checkbox will trigger a click on the link. document.querySelectorAll('.search-form-linked-checkbox').forEach((e) { final checkbox = e.querySelector('input'); final link = e.querySelector('a'); if (checkbox != null && link != null) { final tag = link.dataset['tag']; final action = link.dataset['action']; + if (tag == null) return; Future handleClick(Event event) async { - if (tag != null) { - await _handleInputFieldUpdate( - event, - newQueryFn: (parsedQuery) => parsedQuery.change( - tagsPredicate: parsedQuery.tagsPredicate.toggleRequired(tag), - ), - gtmActionFn: (o, n) => - o.tagsPredicate.hasTag(tag) ? '$action-off' : '$action-on', - ); - } + await _handleInputFieldUpdate( + event, + newQueryFn: (parsedQuery) => parsedQuery.change( + tagsPredicate: parsedQuery.tagsPredicate.toggleRequired(tag), + ), + gtmActionFn: (o, n) => + o.tagsPredicate.hasTag(tag) ? '$action-off' : '$action-on', + ); } checkbox.onChange.listen(handleClick); @@ -126,7 +126,7 @@ Future _handleInputFieldUpdate( await updateBodyWithHttpGet( requestUri: newRequestUri, navigationUrl: newVisibleUri.toString(), - preupdateCheck: () => _lastTargetUri == newVisibleUri, + preUpdateCheck: () => _lastTargetUri == newVisibleUri, ); // notify GTM on the click diff --git a/pkg/web_app/lib/src/web_util.dart b/pkg/web_app/lib/src/web_util.dart index 5fe11d1a40..0a8b751cbf 100644 --- a/pkg/web_app/lib/src/web_util.dart +++ b/pkg/web_app/lib/src/web_util.dart @@ -1,3 +1,7 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + import 'dart:js_interop'; import 'package:web/web.dart'; diff --git a/pkg/web_app/lib/src/widget/completion/widget.dart b/pkg/web_app/lib/src/widget/completion/widget.dart index f184a5598c..52700ba10e 100644 --- a/pkg/web_app/lib/src/widget/completion/widget.dart +++ b/pkg/web_app/lib/src/widget/completion/widget.dart @@ -163,7 +163,7 @@ final class _CompletionWidget { final HTMLInputElement input; final HTMLDivElement dropdown; final CompletionData data; - var state = _State(); + _State state = _State(); _CompletionWidget._({ required this.input, @@ -295,7 +295,7 @@ final class _CompletionWidget { return; } - // If explicily closed using 'Escape', then we handle no more keys + // If explicitly closed using 'Escape', then handle no more keys. if (state.closed) { return; } @@ -311,7 +311,7 @@ final class _CompletionWidget { return; } - // If mouse down on an option element, then we select and apply it + // If mouse down on an option element, then select and apply it. if (event.target.isA()) { final target = event.target as HTMLDivElement; @@ -442,10 +442,10 @@ final class _CompletionWidget { 'Accept': 'application/json', }).timeout(Duration(seconds: 30)), ); - return CompletionData.fromJson(root as Map); + return CompletionData.fromJson(root as Map); } - static late final _canvas = HTMLCanvasElement(); + static final HTMLCanvasElement _canvas = HTMLCanvasElement(); static int getTextWidth(String text, Element element) { final ctx = _canvas.context2D; final style = window.getComputedStyle(element); diff --git a/pkg/web_app/lib/src/widget/switch/widget.dart b/pkg/web_app/lib/src/widget/switch/widget.dart index 09b60880e6..e4ad1d048e 100644 --- a/pkg/web_app/lib/src/widget/switch/widget.dart +++ b/pkg/web_app/lib/src/widget/switch/widget.dart @@ -20,8 +20,8 @@ import '../../web_util.dart'; /// derived from document state. /// /// The optional `data-switch-state-id` property may be used to provide an -/// identifier for the sttae of this switch in `localStorage`. If supplied state -/// will be sync'ed across windows. +/// identifier for the state of this switch in `localStorage`. If supplied state +/// will be synced across windows. /// /// The optional `data-switch-enabled` property may be used to specify a space /// separated list of classes to be applied to `data-switch-target` when the diff --git a/pkg/web_app/pubspec.lock b/pkg/web_app/pubspec.lock index 35bc71ecac..05e3509097 100644 --- a/pkg/web_app/pubspec.lock +++ b/pkg/web_app/pubspec.lock @@ -117,7 +117,7 @@ packages: source: hosted version: "4.10.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf diff --git a/pkg/web_app/pubspec.yaml b/pkg/web_app/pubspec.yaml index 1bdef94d9e..62f63f90ea 100644 --- a/pkg/web_app/pubspec.yaml +++ b/pkg/web_app/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: path: ../_pub_shared api_builder: path: ../api_builder + collection: ^1.18.0 http: ^1.0.0 markdown: ^7.2.0 mdc_web: ^0.6.0 diff --git a/pkg/web_app/test/inner_html_test.dart b/pkg/web_app/test/inner_html_test.dart index 87a06907a8..13dc5255ba 100644 --- a/pkg/web_app/test/inner_html_test.dart +++ b/pkg/web_app/test/inner_html_test.dart @@ -7,7 +7,7 @@ import 'dart:io'; import 'package:test/test.dart'; void main() { - Future _checkFiles(String path) async { + Future _checkFiles(String path) async { final dir = Directory(path); await for (final f in dir.list(recursive: true)) { if (f is File) {