| // Copyright (c) 2014, the Dart project authors. All rights reserved. | |
| // Licensed under the MIT license. | |
| // See LICENSE file in the project root for details. | |
| // TODO(jmesserly): everything in this file is copied straight from "dart:html". | |
| import 'dart:collection'; | |
| import '../dom.dart'; | |
| class ElementCssClassSet extends _CssClassSetImpl { | |
| final Element _element; | |
| ElementCssClassSet(this._element); | |
| @override | |
| Set<String> readClasses() { | |
| final s = LinkedHashSet<String>(); | |
| final classname = _element.className; | |
| for (var name in classname.split(' ')) { | |
| final trimmed = name.trim(); | |
| if (trimmed.isNotEmpty) { | |
| s.add(trimmed); | |
| } | |
| } | |
| return s; | |
| } | |
| @override | |
| void writeClasses(Set<String> s) { | |
| _element.className = s.join(' '); | |
| } | |
| } | |
| /// A Set that stores the CSS class names for an element. | |
| abstract class CssClassSet implements Set<String> { | |
| /// Adds the class [value] to the element if it is not on it, removes it if it | |
| /// is. | |
| /// | |
| /// If [shouldAdd] is true, then we always add that [value] to the element. If | |
| /// [shouldAdd] is false then we always remove [value] from the element. | |
| bool toggle(String value, [bool? shouldAdd]); | |
| /// Returns [:true:] if classes cannot be added or removed from this | |
| /// [:CssClassSet:]. | |
| bool get frozen; | |
| /// Determine if this element contains the class [value]. | |
| /// | |
| /// This is the Dart equivalent of jQuery's | |
| /// [hasClass](http://api.jquery.com/hasClass/). | |
| @override | |
| bool contains(Object? value); | |
| /// Add the class [value] to element. | |
| /// | |
| /// This is the Dart equivalent of jQuery's | |
| /// [addClass](http://api.jquery.com/addClass/). | |
| /// | |
| /// If this corresponds to one element. Returns true if [value] was added to | |
| /// the set, otherwise false. | |
| /// | |
| /// If this corresponds to many elements, null is always returned. | |
| @override | |
| bool add(String value); | |
| /// Remove the class [value] from element, and return true on successful | |
| /// removal. | |
| /// | |
| /// This is the Dart equivalent of jQuery's | |
| /// [removeClass](http://api.jquery.com/removeClass/). | |
| @override | |
| bool remove(Object? value); | |
| /// Add all classes specified in [iterable] to element. | |
| /// | |
| /// This is the Dart equivalent of jQuery's | |
| /// [addClass](http://api.jquery.com/addClass/). | |
| @override | |
| void addAll(Iterable<String> iterable); | |
| /// Remove all classes specified in [iterable] from element. | |
| /// | |
| /// This is the Dart equivalent of jQuery's | |
| /// [removeClass](http://api.jquery.com/removeClass/). | |
| @override | |
| void removeAll(Iterable<Object?> iterable); | |
| /// Toggles all classes specified in [iterable] on element. | |
| /// | |
| /// Iterate through [iterable]'s items, and add it if it is not on it, or | |
| /// remove it if it is. This is the Dart equivalent of jQuery's | |
| /// [toggleClass](http://api.jquery.com/toggleClass/). | |
| /// If [shouldAdd] is true, then we always add all the classes in [iterable] | |
| /// element. If [shouldAdd] is false then we always remove all the classes in | |
| /// [iterable] from the element. | |
| void toggleAll(Iterable<String> iterable, [bool? shouldAdd]); | |
| } | |
| abstract class _CssClassSetImpl extends SetBase<String> implements CssClassSet { | |
| @override | |
| String toString() { | |
| return readClasses().join(' '); | |
| } | |
| /// Adds the class [value] to the element if it is not on it, removes it if it | |
| /// is. | |
| /// | |
| /// If [shouldAdd] is true, then we always add that [value] to the element. If | |
| /// [shouldAdd] is false then we always remove [value] from the element. | |
| @override | |
| bool toggle(String value, [bool? shouldAdd]) { | |
| final s = readClasses(); | |
| var result = false; | |
| shouldAdd ??= !s.contains(value); | |
| if (shouldAdd) { | |
| s.add(value); | |
| result = true; | |
| } else { | |
| s.remove(value); | |
| } | |
| writeClasses(s); | |
| return result; | |
| } | |
| /// Returns [:true:] if classes cannot be added or removed from this | |
| /// [:CssClassSet:]. | |
| @override | |
| bool get frozen => false; | |
| @override | |
| Iterator<String> get iterator => readClasses().iterator; | |
| @override | |
| int get length => readClasses().length; | |
| // interface Set - BEGIN | |
| /// Determine if this element contains the class [value]. | |
| /// | |
| /// This is the Dart equivalent of jQuery's | |
| /// [hasClass](http://api.jquery.com/hasClass/). | |
| @override | |
| bool contains(Object? value) => readClasses().contains(value); | |
| /// Lookup from the Set interface. Not interesting for a String set. | |
| @override | |
| String? lookup(Object? value) => contains(value) ? value as String? : null; | |
| @override | |
| Set<String> toSet() => readClasses().toSet(); | |
| /// Add the class [value] to element. | |
| /// | |
| /// This is the Dart equivalent of jQuery's | |
| /// [addClass](http://api.jquery.com/addClass/). | |
| @override | |
| bool add(String value) { | |
| // TODO - figure out if we need to do any validation here | |
| // or if the browser natively does enough. | |
| return _modify((s) => s.add(value)); | |
| } | |
| /// Remove the class [value] from element, and return true on successful | |
| /// removal. | |
| /// | |
| /// This is the Dart equivalent of jQuery's | |
| /// [removeClass](http://api.jquery.com/removeClass/). | |
| @override | |
| bool remove(Object? value) { | |
| if (value is! String) return false; | |
| final s = readClasses(); | |
| final result = s.remove(value); | |
| writeClasses(s); | |
| return result; | |
| } | |
| /// Toggles all classes specified in [iterable] on element. | |
| /// | |
| /// Iterate through [iterable]'s items, and add it if it is not on it, or | |
| /// remove it if it is. This is the Dart equivalent of jQuery's | |
| /// [toggleClass](http://api.jquery.com/toggleClass/). | |
| /// If [shouldAdd] is true, then we always add all the classes in [iterable] | |
| /// element. If [shouldAdd] is false then we always remove all the classes in | |
| /// [iterable] from the element. | |
| @override | |
| void toggleAll(Iterable<String> iterable, [bool? shouldAdd]) { | |
| for (var e in iterable) { | |
| toggle(e, shouldAdd); | |
| } | |
| } | |
| /// Helper method used to modify the set of css classes on this element. | |
| /// | |
| /// f - callback with: | |
| /// s - a Set of all the css class name currently on this element. | |
| /// | |
| /// After f returns, the modified set is written to the | |
| /// className property of this element. | |
| bool _modify(bool Function(Set<String>) f) { | |
| final s = readClasses(); | |
| final ret = f(s); | |
| writeClasses(s); | |
| return ret; | |
| } | |
| /// Read the class names from the Element class property, | |
| /// and put them into a set (duplicates are discarded). | |
| /// This is intended to be overridden by specific implementations. | |
| Set<String> readClasses(); | |
| /// Join all the elements of a set into one string and write | |
| /// back to the element. | |
| /// This is intended to be overridden by specific implementations. | |
| void writeClasses(Set<String> s); | |
| } |