Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add value multiplication suffix feature #1383

Merged
merged 1 commit into from
Jan 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions projects/libs/flex-layout/core/multiply/multiplier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export interface Multiplier {
readonly unit: string;
readonly value: number;
}

const MULTIPLIER_SUFFIX = 'x';

export function multiply(value: string, multiplier?: Multiplier): string {
if (multiplier === undefined) {
return value;
}

const transformValue = (possibleValue: string) => {
const numberValue = +(possibleValue.slice(0, -MULTIPLIER_SUFFIX.length));

if (value.endsWith(MULTIPLIER_SUFFIX) && !isNaN(numberValue)) {
return `${numberValue * multiplier.value}${multiplier.unit}`;
}

return value;
};

return value.includes(' ') ?
value.split(' ').map(transformValue).join(' ') : transformValue(value);
}
1 change: 1 addition & 0 deletions projects/libs/flex-layout/core/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export * from './style-builder/style-builder';
export * from './basis-validator/basis-validator';
export * from './media-marshaller/media-marshaller';
export * from './media-marshaller/print-hook';
export {Multiplier, multiply as ɵmultiply} from './multiply/multiplier';
8 changes: 7 additions & 1 deletion projects/libs/flex-layout/core/tokens/library-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken} from '@angular/core';
import {Multiplier} from '../multiply/multiplier';

/** a set of configuration options for FlexLayoutModule */
export interface LayoutConfigOptions {
Expand All @@ -18,9 +19,10 @@ export interface LayoutConfigOptions {
printWithBreakpoints?: string[];
mediaTriggerAutoRestore?: boolean;
ssrObserveBreakpoints?: string[];
multiplier?: Multiplier;
}

export const DEFAULT_CONFIG: LayoutConfigOptions = {
export const DEFAULT_CONFIG: Required<LayoutConfigOptions> = {
addFlexToParent: true,
addOrientationBps: false,
disableDefaultBps: false,
Expand All @@ -30,6 +32,10 @@ export const DEFAULT_CONFIG: LayoutConfigOptions = {
printWithBreakpoints: [],
mediaTriggerAutoRestore: true,
ssrObserveBreakpoints: [],
// This is disabled by default because otherwise the multiplier would
// run for all users, regardless of whether they're using this feature.
// Instead, we disable it by default, which requires this ugly cast.
multiplier: undefined as unknown as Multiplier,
};

export const LAYOUT_CONFIG = new InjectionToken<LayoutConfigOptions>(
Expand Down
16 changes: 15 additions & 1 deletion projects/libs/flex-layout/flex/flex-offset/flex-offset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ describe('flex-offset directive', () => {

// Configure testbed to prepare services
TestBed.configureTestingModule({
imports: [CommonModule, FlexLayoutModule],
imports: [CommonModule, FlexLayoutModule.withConfig({
multiplier: {
value: 4,
unit: 'px',
},
})],
declarations: [TestFlexComponent],
providers: [
{provide: DIR_DOCUMENT, useValue: fakeDocument},
Expand All @@ -67,6 +72,15 @@ describe('flex-offset directive', () => {
expectEl(dom).toHaveStyle({'flex': '1 1 0%'}, styler);
});

it('should add correct styles for default `fxFlexOffset` usage w/ mulitplier', () => {
componentWithTemplate(`<div fxFlexOffset='8x' fxFlex></div>`);
fixture.detectChanges();

let dom = fixture.debugElement.children[0];
expectEl(dom).toHaveStyle({'margin-left': '32px'}, styler);
expectEl(dom).toHaveStyle({'flex': '1 1 0%'}, styler);
});


it('should work with percentage values', () => {
componentWithTemplate(`<div fxFlexOffset='17' fxFlex='37'></div>`);
Expand Down
20 changes: 13 additions & 7 deletions projects/libs/flex-layout/flex/flex-offset/flex-offset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, OnChanges, Injectable} from '@angular/core';
import {Directive, ElementRef, OnChanges, Injectable, Inject} from '@angular/core';
import {Directionality} from '@angular/cdk/bidi';
import {
MediaMarshaller,
BaseDirective2,
StyleBuilder,
StyleDefinition,
StyleUtils,
ɵmultiply as multiply,
LAYOUT_CONFIG,
LayoutConfigOptions,
} from '@angular/flex-layout/core';
import {isFlowHorizontal} from '@angular/flex-layout/_private-utils';
import {takeUntil} from 'rxjs/operators';

import {isFlowHorizontal} from '@angular/flex-layout/_private-utils';

export interface FlexOffsetParent {
layout: string;
Expand All @@ -25,18 +28,21 @@ export interface FlexOffsetParent {

@Injectable({providedIn: 'root'})
export class FlexOffsetStyleBuilder extends StyleBuilder {
constructor(@Inject(LAYOUT_CONFIG) private _config: LayoutConfigOptions) {
super();
}

buildStyles(offset: string, parent: FlexOffsetParent) {
if (offset === '') {
offset = '0';
}
offset ||= '0';
offset = multiply(offset, this._config.multiplier);
const isPercent = String(offset).indexOf('%') > -1;
const isPx = String(offset).indexOf('px') > -1;
if (!isPx && !isPercent && !isNaN(+offset)) {
offset = offset + '%';
offset = `${offset}%`;
}
const horizontalLayoutKey = parent.isRtl ? 'margin-right' : 'margin-left';
const styles: StyleDefinition = isFlowHorizontal(parent.layout) ?
{[horizontalLayoutKey]: `${offset}`} : {'margin-top': `${offset}`};
{[horizontalLayoutKey]: offset} : {'margin-top': offset};

return styles;
}
Expand Down
53 changes: 49 additions & 4 deletions projects/libs/flex-layout/flex/layout-gap/layout-gap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
import {Component, Injectable, OnInit, PLATFORM_ID} from '@angular/core';
import {CommonModule, isPlatformServer} from '@angular/common';
import {TestBed, ComponentFixture, inject, async} from '@angular/core/testing';
import {TestBed, ComponentFixture, inject, waitForAsync} from '@angular/core/testing';
import {DIR_DOCUMENT} from '@angular/cdk/bidi';
import {
ɵMatchMedia as MatchMedia,
Expand Down Expand Up @@ -51,7 +51,12 @@ describe('layout-gap directive', () => {

// Configure testbed to prepare services
TestBed.configureTestingModule({
imports: [CommonModule, FlexLayoutModule],
imports: [CommonModule, FlexLayoutModule.withConfig({
multiplier: {
value: 4,
unit: 'px',
}
})],
declarations: [TestLayoutGapComponent],
providers: [
MockMatchMediaProvider,
Expand Down Expand Up @@ -111,6 +116,25 @@ describe('layout-gap directive', () => {
expectEl(nodes[2]).not.toHaveStyle({'margin-right': '0px'}, styler);
});

it('should add gap styles to all children except the 1st child w/ multiplier', () => {
let template = `
<div fxLayoutAlign='center center' fxLayoutGap='13x'>
<div fxFlex></div>
<div fxFlex></div>
<div fxFlex></div>
</div>
`;
createTestComponent(template);
fixture.detectChanges();

let nodes = queryFor(fixture, '[fxFlex]');
expect(nodes.length).toEqual(3);
expectEl(nodes[0]).toHaveStyle({'margin-right': '52px'}, styler);
expectEl(nodes[1]).toHaveStyle({'margin-right': '52px'}, styler);
expectEl(nodes[2]).not.toHaveStyle({'margin-right': '52px'}, styler);
expectEl(nodes[2]).not.toHaveStyle({'margin-right': '0px'}, styler);
});

it('should add gap styles in proper order when order style is applied', () => {
let template = `
<div fxLayoutAlign='center center' fxLayoutGap='13px'>
Expand Down Expand Up @@ -149,7 +173,7 @@ describe('layout-gap directive', () => {
expectEl(nodes[3]).not.toHaveStyle({'margin-right': '0px'}, styler);
});

it('should add update gap styles when row items are removed', async(() => {
it('should add update gap styles when row items are removed', waitForAsync(() => {
let template = `
<div fxLayoutAlign='center center' fxLayoutGap='13px'>
<div fxFlex *ngFor='let row of rows'></div>
Expand Down Expand Up @@ -181,7 +205,7 @@ describe('layout-gap directive', () => {

}));

it('should add update gap styles when only 1 row is remaining', async(() => {
it('should add update gap styles when only 1 row is remaining', waitForAsync(() => {
let template = `
<div fxLayoutAlign='center center' fxLayoutGap='13px'>
<div fxFlex *ngFor='let row of rows'></div>
Expand Down Expand Up @@ -509,6 +533,27 @@ describe('layout-gap directive', () => {
expectNativeEl(fixture).toHaveStyle(expectedMargin, styler);
});

it('should add gap styles correctly w/ multiplier', () => {
let template = `
<div fxLayoutGap='13x grid'>
<div fxFlex></div>
<div fxFlex></div>
<div fxFlex></div>
</div>
`;
createTestComponent(template);
fixture.detectChanges();

let nodes = queryFor(fixture, '[fxFlex]');
let expectedMargin = {'margin': '0px -52px -52px 0px'};
let expectedPadding = {'padding': '0px 52px 52px 0px'};
expect(nodes.length).toEqual(3);
expectEl(nodes[0]).toHaveStyle(expectedPadding, styler);
expectEl(nodes[1]).toHaveStyle(expectedPadding, styler);
expectEl(nodes[2]).toHaveStyle(expectedPadding, styler);
expectNativeEl(fixture).toHaveStyle(expectedMargin, styler);
});

it('should add gap styles correctly between option', () => {
let template = `
<div fxLayoutGap='13px 12px grid'>
Expand Down
16 changes: 12 additions & 4 deletions projects/libs/flex-layout/flex/layout-gap/layout-gap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
NgZone,
Injectable,
AfterContentInit,
Inject,
} from '@angular/core';
import {Directionality} from '@angular/cdk/bidi';
import {
Expand All @@ -21,11 +22,14 @@ import {
StyleUtils,
MediaMarshaller,
ElementMatcher,
LAYOUT_CONFIG,
LayoutConfigOptions,
ɵmultiply as multiply,
} from '@angular/flex-layout/core';
import {LAYOUT_VALUES} from '@angular/flex-layout/_private-utils';
import {Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';

import {LAYOUT_VALUES} from '@angular/flex-layout/_private-utils';

export interface LayoutGapParent {
directionality: string;
Expand All @@ -42,13 +46,15 @@ const CLEAR_MARGIN_CSS = {

@Injectable({providedIn: 'root'})
export class LayoutGapStyleBuilder extends StyleBuilder {
constructor(private _styler: StyleUtils) {
constructor(private _styler: StyleUtils,
@Inject(LAYOUT_CONFIG) private _config: LayoutConfigOptions) {
super();
}

buildStyles(gapValue: string, parent: LayoutGapParent) {
if (gapValue.endsWith(GRID_SPECIFIER)) {
gapValue = gapValue.slice(0, gapValue.indexOf(GRID_SPECIFIER));
gapValue = multiply(gapValue, this._config.multiplier);

// Add the margin to the host element
return buildGridMargin(gapValue, parent.directionality);
Expand All @@ -61,10 +67,12 @@ export class LayoutGapStyleBuilder extends StyleBuilder {
const items = parent.items;
if (gapValue.endsWith(GRID_SPECIFIER)) {
gapValue = gapValue.slice(0, gapValue.indexOf(GRID_SPECIFIER));
gapValue = multiply(gapValue, this._config.multiplier);
// For each `element` children, set the padding
const paddingStyles = buildGridPadding(gapValue, parent.directionality);
this._styler.applyStyleToElements(paddingStyles, parent.items);
} else {
gapValue = multiply(gapValue, this._config.multiplier);
const lastItem = items.pop()!;

// For each `element` children EXCEPT the last,
Expand Down Expand Up @@ -253,7 +261,7 @@ const GRID_SPECIFIER = ' grid';

function buildGridPadding(value: string, directionality: string): StyleDefinition {
const [between, below] = value.split(' ');
const bottom = below || between;
const bottom = below ?? between;
let paddingRight = '0px', paddingBottom = bottom, paddingLeft = '0px';

if (directionality === 'rtl') {
Expand All @@ -267,7 +275,7 @@ function buildGridPadding(value: string, directionality: string): StyleDefinitio

function buildGridMargin(value: string, directionality: string): StyleDefinition {
const [between, below] = value.split(' ');
const bottom = below || between;
const bottom = below ?? between;
const minus = (str: string) => `-${str}`;
let marginRight = '0px', marginBottom = minus(bottom), marginLeft = '0px';

Expand Down