Skip to content

Commit

Permalink
Improve some doc comments
Browse files Browse the repository at this point in the history
+ That were discovered while doing the memory leak audit
  • Loading branch information
aaronlademann-wf committed Jul 17, 2017
1 parent bc46e9c commit 1a0d6bc
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 151 deletions.
3 changes: 1 addition & 2 deletions lib/src/component/resize_sensor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import 'dart:html';

import 'package:meta/meta.dart';
import 'package:platform_detect/platform_detect.dart';
import 'package:react/react.dart' as react;
import 'package:over_react/over_react.dart';

/// A wrapper component that detects when its parent is resized.
Expand Down Expand Up @@ -189,7 +188,7 @@ class ResizeSensorComponent extends UiComponent<ResizeSensorProps> with _SafeAni

/// When the expand or collapse sensors are resized, builds a [ResizeSensorEvent] and calls
/// props.onResize with it. Then, calls through to [_reset()].
void _handleSensorScroll(react.SyntheticEvent _) {
void _handleSensorScroll(SyntheticEvent _) {
if (_scrollEventsToIgnore > 0) {
_scrollEventsToIgnore--;
return;
Expand Down
163 changes: 123 additions & 40 deletions lib/src/component_declaration/component_base.dart

Large diffs are not rendered by default.

52 changes: 44 additions & 8 deletions lib/src/component_declaration/component_type_checking.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
library over_react.component_declaration.component_type_checking;

import 'package:over_react/src/component_declaration/component_base.dart' show UiFactory;
import 'package:over_react/src/component_declaration/annotations.dart' as annotations show Component;
import 'package:over_react/src/util/react_wrappers.dart';
import 'package:react/react_client.dart';
import 'package:react/react_client/js_interop_helpers.dart';
import 'package:react/react_client/react_interop.dart';

// ----------------------------------------------------------------------
// Component type registration and internal type metadata management
Expand Down Expand Up @@ -73,10 +75,40 @@ class ComponentTypeMeta {
///
/// Used to enable inheritance in component type-checking in [isComponentOfType].
///
/// E.g., if component `Bar` is a subtype of component `Foo`, then:
/// E.g., if component `Bar` is a subtype of component `Foo`:
///
/// //
/// // foo.dart
/// //
///
/// @Factory()
/// UiFactory<FooProps> Foo;
///
/// @Component()
/// class FooComponent extends UiComponent<FooProps> {
/// // ...
/// }
///
/// //
/// // bar.dart
/// //
///
/// @Factory()
/// UiFactory<FooProps> Foo;
///
/// @Component(subtypeOf: FooComponent)
/// class BarComponent extends UiComponent<BarProps> {
/// // ...
/// }
///
/// //
/// // app.dart
/// //
///
/// isComponentOfType(Bar()(), Bar); // true (due to normal type-checking)
/// isComponentOfType(Bar()(), Foo); // true (due to parent type-checking)
///
/// > See: `subtypeOf` (within [annotations.Component])
final ReactDartComponentFactoryProxy parentType;

ComponentTypeMeta(this.isWrapper, this.parentType);
Expand Down Expand Up @@ -108,7 +140,7 @@ class ComponentTypeMeta {
///
/// Consumers of this function should be sure to take the latter case into consideration.
///
/// __CAVEAT:__ Due to type-checking limitations on JS-interop types, when [typeAlias] is a [Function],
/// > __CAVEAT:__ Due to type-checking limitations on JS-interop types, when [typeAlias] is a [Function],
/// and it is not found to be an alias for another type, it will be returned as if it were a valid type.
dynamic getComponentTypeFromAlias(dynamic typeAlias) {
/// If `typeAlias` is a factory, return its type.
Expand Down Expand Up @@ -143,19 +175,19 @@ dynamic getComponentTypeFromAlias(dynamic typeAlias) {
/// * [String] tag name (DOM components)
/// * [Function] ([ReactClass]) factory (Dart/JS composite components)
///
/// Note: It's impossible to determine know whether something is a ReactClass due to type-checking restrictions
/// for JS-interop classes, so a Function type-check is the best we can do.
/// > __NOTE:__ It's impossible to determine know whether something is a [ReactClass] due to type-checking restrictions
/// for JS-interop classes, so a Function type-check is the best we can do.
bool isPotentiallyValidComponentType(dynamic type) {
return type is Function || type is String;
}

/// Returns an [Iterable] of all component types that are ancestors of [typeAlias].
/// Returns an [Iterable] of all component types that are ancestors of [type].
///
/// For example, given components A, B, and C, where B subtypes A and C subtypes B:
///
/// getParentTypes(getTypeFromAlias(A)); // []
/// getParentTypes(getTypeFromAlias(B)); // [A].map(getTypeFromAlias)
/// getParentTypes(getTypeFromAlias(C)); // [B, A].map(getTypeFromAlias)
/// getParentTypes(getComponentTypeFromAlias(A)); // []
/// getParentTypes(getComponentTypeFromAlias(B)); // [A].map(getTypeFromAlias)
/// getParentTypes(getComponentTypeFromAlias(C)); // [B, A].map(getTypeFromAlias)
Iterable<dynamic> getParentTypes(dynamic type) sync* {
assert(isPotentiallyValidComponentType(type) &&
'`type` should be a valid component type (and not null or a type alias).' is String);
Expand All @@ -182,6 +214,8 @@ Iterable<dynamic> getParentTypes(dynamic type) sync* {
/// * [ReactComponentFactoryProxy]
/// * [ReactClass] component factory
/// * [String] tag name (DOM components only)
///
/// > Related: [isValidElementOfType]
bool isComponentOfType(ReactElement instance, dynamic typeAlias, {
bool traverseWrappers: true,
bool matchParentTypes: true
Expand Down Expand Up @@ -228,6 +262,8 @@ bool isComponentOfType(ReactElement instance, dynamic typeAlias, {
/// * [ReactComponentFactoryProxy]
/// * [ReactClass] component factory
/// * [String] tag name (DOM components only)
///
/// > Related: [isComponentOfType]
bool isValidElementOfType(dynamic instance, dynamic typeAlias) {
return isValidElement(instance) && isComponentOfType(instance, typeAlias);
}
8 changes: 6 additions & 2 deletions lib/src/component_declaration/flux_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ abstract class FluxUiProps<ActionsT, StoresT> extends UiProps {
/// the resulting component.
///
/// Use with the over_react transformer via the `@Component()` ([annotations.Component]) annotation.
///
/// > Related: [FluxUiStatefulComponent]
abstract class FluxUiComponent<TProps extends FluxUiProps> extends UiComponent<TProps>
with _FluxComponentMixin<TProps>, BatchedRedraws {
// Redeclare these lifecycle methods with `mustCallSuper`, since `mustCallSuper` added to methods within
Expand Down Expand Up @@ -102,6 +104,8 @@ abstract class FluxUiComponent<TProps extends FluxUiProps> extends UiComponent<T
/// the resulting component.
///
/// Use with the over_react transformer via the `@Component()` ([annotations.Component]) annotation.
///
/// > Related: [FluxUiComponent]
abstract class FluxUiStatefulComponent<TProps extends FluxUiProps, TState extends UiState>
extends UiStatefulComponent<TProps, TState>
with _FluxComponentMixin<TProps>, BatchedRedraws {
Expand Down Expand Up @@ -156,9 +160,9 @@ abstract class _FluxComponentMixin<TProps extends FluxUiProps> implements Batche

handlers.forEach((store, handler) {
String message = 'Cannot listen to a disposed/disposing Store.';

var isDisposedOrDisposing = store.isDisposedOrDisposing ?? false;

assert(!isDisposedOrDisposing, '$message This can be caused by BatchedRedraws '
'mounting the component asynchronously after the store has been disposed. If you are '
'in a test environment, try adding an `await window.animationFrame;` before disposing your '
Expand Down
51 changes: 31 additions & 20 deletions lib/src/util/class_names.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,25 @@ class CssClassPropsMapView extends MapView with CssClassPropsMixin {
Map get props => this;
}

/// StringBuffer-backed className builder optimized for adding classNames, with support for blacklisting CSS classes.
/// [StringBuffer]-backed CSS className builder optimized for adding classNames,
/// with support for blacklisting CSS classes.
class ClassNameBuilder {
StringBuffer _classNamesBuffer = new StringBuffer();
StringBuffer _blacklistBuffer;

/// Creates a new, empty ClassNameBuilder.
ClassNameBuilder();

/// Creates a new ClassNameBuilder with className and blacklist values added from [CssClassProps.className] and
/// [CssClassProps.classNameBlackList], if they are specified.
/// Creates a new ClassNameBuilder with className and blacklist values added from [CssClassPropsMixin.className] and
/// [CssClassPropsMixin.classNameBlacklist], if they are specified within the provided [props] Map.
///
/// This method gracefully handles null [props], as well as unspecified/null prop values.
ClassNameBuilder.fromProps(Map props) {
addFromProps(props);
}

/// Adds the className and blacklist values from a [props] Map, using the
/// [CssClassProps.className] and [CssClassProps.classNameBlackList] values.
/// Adds the className and blacklist values from the provided [props] Map, using the
/// [CssClassPropsMixin.className] and [CssClassPropsMixin.classNameBlacklist] values.
///
/// This method gracefully handles null [props], as well as unspecified/null prop values.
///
Expand All @@ -87,10 +88,12 @@ class ClassNameBuilder {
..blacklist(cssClassProps.classNameBlacklist);
}

/// Adds a className string. May be a single CSS class 'token', or multiple space-delimited classes,
/// IF [should] is true, otherwise, does nothing (convenience for helping to inline addition conditionals).
/// Adds all of the CSS classes represented by [className] _(a space-delimited list)_ to the [_classNamesBuffer] -
/// only if [should] is true.
///
/// There is no checking for duplicate CSS classes.
/// Otherwise, does nothing _(convenience for helping to inline addition conditionals)_.
///
/// > There is no checking for duplicate CSS classes.
void add(String className, [bool should = true]) {
if (!should || className == null || className == '') {
return;
Expand All @@ -102,8 +105,10 @@ class ClassNameBuilder {
_classNamesBuffer.write(className);
}

/// Adds all of the CSS classes represented by [className] (a space-delimited list) to the blacklist,
/// IF [should] is true, otherwise, does nothing (convenience for helping to inline blacklisting conditionals).
/// Adds all of the CSS classes represented by [className] _(a space-delimited list)_ to the [_blacklistBuffer] -
/// only if [should] is true.
///
/// Otherwise, does nothing _(convenience for helping to inline blacklisting conditionals)_.
///
/// Classes added to the blacklist will not appear in the result of [toClassName].
void blacklist(String className, [bool should = true]) {
Expand All @@ -121,34 +126,40 @@ class ClassNameBuilder {
_blacklistBuffer.write(className);
}

/// Returns a String representation of the built className, which includes any added classes, and none of the blacklisted classes.
/// Returns a String representation of the built className, which includes any added classes,
/// and none of the blacklisted classes.
///
/// > There is no checking for duplicate CSS classes.
///
/// Duplicate classes will be added.
/// > Related: [toClassNameBlacklist]
String toClassName() {
String className = _classNamesBuffer.toString();

if (_blacklistBuffer != null && _blacklistBuffer.isNotEmpty) {
List blacklistedClasses = splitSpaceDelimitedString(_blacklistBuffer.toString());

className = splitSpaceDelimitedString(className)
.where((String cssClass) => !blacklistedClasses.contains(cssClass))
.join(' ');
.where((String cssClass) => !blacklistedClasses.contains(cssClass))
.join(' ');
}

return className;
}

/// Returns a String representation of only the blacklisted classes.
///
/// Useful for blacklist forwarding.
///
/// Duplicate classes will be added.
/// > There is no checking for duplicate CSS classes.
///
/// > Related: [toClassName]
String toClassNameBlacklist() {
return _blacklistBuffer == null || _blacklistBuffer.isEmpty
? null
: _blacklistBuffer.toString();
? null
: _blacklistBuffer.toString();
}

/// Returns a Map with the [CssClassProps.className] and [CssClassProps.classNameBlackList] props
/// Returns a Map with the [CssClassPropsMixin.className] and [CssClassPropsMixin.classNameBlacklist] props
/// populated from the return values of [toClassName] and [toClassNameBlacklist], respectively.
///
/// This method, along with [addFromProps], is useful for merging sets of className/blacklist props.
Expand All @@ -164,9 +175,9 @@ class ClassNameBuilder {
}
}

/// Returns a List of space-delimited tokens efficiently split from the specified string.
/// Returns a List of space-delimited tokens efficiently split from the specified [string].
///
/// Useful for splitting CSS class name strings into class tokens, or `data-test-id` values into individual test IDs.
/// Useful for splitting CSS className strings into class tokens, or `data-test-id` values into individual test IDs.
///
/// Handles leading and trailing spaces, as well as token separated by multiple spaces.
///
Expand Down
15 changes: 11 additions & 4 deletions lib/src/util/css_value_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ library over_react.css_value_util;

import 'package:quiver/core.dart';

/// A CSS length value, with a number and unit component, for use in CSS properties such as `width`, `top`, `padding`, etc.
/// A CSS length value, with a number and unit component,
/// for use in CSS properties such as `width`, `top`, `padding`, etc.
class CssValue implements Comparable<CssValue> {
/// The number component of this CSS value.
///
Expand All @@ -28,14 +29,20 @@ class CssValue implements Comparable<CssValue> {
/// E.g., 'px' for '1px'
final String unit;

/// Creates a new [CssValue]. If no [unit] is specified, `'px'` is used instead.
/// Creates a new [CssValue].
///
/// If no [unit] is specified, `'px'` is used instead.
const CssValue(this.number, [this.unit = 'px']);

/// Parse [source] and return its [CssValue] representation.
///
/// Accepts a number optionally followed by a CSS length unit. If no unit is present, `'px'` is used as the unit instead.
/// Accepts a number optionally followed by a CSS length unit.
/// If no unit is present, `'px'` is used as the unit instead.
///
/// If `source` is not a valid CSS value, the [onError] callback is called with [source] and an error object, and its return value is used instead. If no `onError` is provided, `null` is returned.
/// If `source` is not a valid CSS value, the [onError] callback
/// is called with [source] and an error object, and its return
/// value is used instead. If no `onError` is provided, `null`
/// is returned.
///
/// Examples of accepted values:
///
Expand Down
7 changes: 3 additions & 4 deletions lib/src/util/dom_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Iterable<Element> _hierarchy(Element element) sync* {
}

/// Returns the closest element in the hierarchy of [lowerBound] up to an optional [upperBound] (both inclusive)
/// that matches [selector], or `null if no matches are found.
/// that matches [selector], or `null` if no matches are found.
Element closest(Element lowerBound, String selector, {Element upperBound}) {
for (var element in _hierarchy(lowerBound)) {
if (element.matches(selector)) return element;
Expand All @@ -48,7 +48,8 @@ Element closest(Element lowerBound, String selector, {Element upperBound}) {
return null;
}

/// Returns the currently focused element, or `null` if there is none.
/// Returns the currently focused element ([HtmlDocument.activeElement]),
/// or `null` if nothing is focused (e.g. [HtmlDocument.activeElement] is [BodyElement]).
Element getActiveElement() {
var activeElement = document.activeElement;

Expand All @@ -62,8 +63,6 @@ Element getActiveElement() {
/// Necessary because of the circular inheritance hierarchy in Dart's [InputElement] class structure.
///
/// See: <https://github.com/dart-lang/sdk/issues/22967>
///
/// Related: [isTextInputElementBase]
const List<String> inputTypesWithSelectionRangeSupport = const [
'search',
'text',
Expand Down
26 changes: 13 additions & 13 deletions lib/src/util/event_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ library over_react.event_helpers;

import 'dart:html';

import 'package:react/react.dart' as react;
import 'package:over_react/over_react.dart';

/// Helper util that wraps a native [KeyboardEvent] in a [react.SyntheticKeyboardEvent].
/// Helper util that wraps a native [KeyboardEvent] in a [SyntheticKeyboardEvent].
///
/// Used where a native [KeyboardEvent] is given and a [react.SyntheticKeyboardEvent] is needed.
react.SyntheticKeyboardEvent wrapNativeKeyboardEvent(KeyboardEvent nativeKeyboardEvent) {
return new react.SyntheticKeyboardEvent(
/// Used where a native [KeyboardEvent] is given and a [SyntheticKeyboardEvent] is needed.
SyntheticKeyboardEvent wrapNativeKeyboardEvent(KeyboardEvent nativeKeyboardEvent) {
return new SyntheticKeyboardEvent(
nativeKeyboardEvent.bubbles,
nativeKeyboardEvent.cancelable,
nativeKeyboardEvent.currentTarget,
Expand All @@ -48,11 +48,11 @@ react.SyntheticKeyboardEvent wrapNativeKeyboardEvent(KeyboardEvent nativeKeyboar
nativeKeyboardEvent.shiftKey);
}

/// Helper util that wraps a native [MouseEvent] in a [react.MouseEvent].
/// Helper util that wraps a native [MouseEvent] in a [SyntheticMouseEvent].
///
/// Used where a native [MouseEvent] is given and a [react.MouseEvent] is needed.
react.SyntheticMouseEvent wrapNativeMouseEvent(MouseEvent nativeMouseEvent) {
return new react.SyntheticMouseEvent(
/// Used where a native [MouseEvent] is given and a [SyntheticMouseEvent] is needed.
SyntheticMouseEvent wrapNativeMouseEvent(MouseEvent nativeMouseEvent) {
return new SyntheticMouseEvent(
nativeMouseEvent.bubbles,
nativeMouseEvent.cancelable,
nativeMouseEvent.currentTarget,
Expand Down Expand Up @@ -83,12 +83,12 @@ react.SyntheticMouseEvent wrapNativeMouseEvent(MouseEvent nativeMouseEvent) {
}

/// If the consumer specifies a callback like `onChange` on one of our custom form components that are not *actually*
/// form elements - we still need a valid [react.SyntheticFormEvent] to pass as the expected parameter to that callback.
/// form elements - we still need a valid [SyntheticFormEvent] to pass as the expected parameter to that callback.
///
/// This helper method generates a "fake" [react.SyntheticFormEvent], with nothing but the `target` set to [element],
/// This helper method generates a "fake" [SyntheticFormEvent], with nothing but the `target` set to [element],
/// `type` set to [type] and `timeStamp` set to the current time. All other arguments are `noop`, `false` or `null`.
react.SyntheticFormEvent fakeSyntheticFormEvent(Element element, String type) {
return new react.SyntheticFormEvent(
SyntheticFormEvent fakeSyntheticFormEvent(Element element, String type) {
return new SyntheticFormEvent(
false,
false,
element,
Expand Down
Loading

0 comments on commit 1a0d6bc

Please sign in to comment.