@enact/ui
Version:
A collection of simplified unstyled cross-platform UI components for Enact
464 lines (411 loc) • 15.1 kB
text/less
@import "./utils.less";
@import "./variables.less";
// Disabled elements
.disabled(@rules; @target) when (isruleset(@rules)) and (@target = parent) {
[disabled] {
@rules();
}
}
.disabled(@rules) when (isruleset(@rules)) {
&[disabled] {
@rules();
}
}
.enact-composite() {
transform: translateZ(0);
will-change: transform;
}
// Applies RTL-compatible start and end position to a selector
// Simple "when" here just assumes that if the first argument is a list with more than 1 entry,
// then units are correct. If it's just 1, then we verify the value is a number (measurement).
.position-start-end (@trbl; @target: "") when (length(@trbl) > 1) or (@target = parent) or (@target = self) or (@target = "") {
.position(@trbl);
.enact-locale-rtl({
.position(
// Note: swapping left and right values
.extract(@trbl; top)[]
.extract(@trbl; left)[]
.extract(@trbl; bottom)[]
.extract(@trbl; right)[]
);
}; @target);
}
.position-start-end (@start; @end; @target: "") when (default()) {
left: @start;
right: @end;
.enact-locale-rtl({
left: @end;
right: @start;
}, @target);
}
// Applies RTL-compatible start and end margins to a selector
.margin-start-end (@trbl; @target: "") when (length(@trbl) > 1) or (@target = parent) or (@target = self) or (@target = "") {
margin: @trbl;
.enact-locale-rtl({
margin:
// Note: swapping left and right values
.extract(@trbl; top)[]
.extract(@trbl; left)[]
.extract(@trbl; bottom)[]
.extract(@trbl; right)[]
;
}; @target);
}
.margin-start-end(@start; @end; @target: "") when (default()) {
margin-left: @start;
margin-right: @end;
.enact-locale-rtl({
margin-left: @end;
margin-right: @start;
}, @target);
}
// Applies RTL-compatible start and end padding to a selector
.padding-start-end (@trbl; @target: "") when (length(@trbl) > 1) or (@target = parent) or (@target = self) or (@target = "") {
padding: @trbl;
.enact-locale-rtl({
padding:
// Note: swapping left and right values
.extract(@trbl; top)[]
.extract(@trbl; left)[]
.extract(@trbl; bottom)[]
.extract(@trbl; right)[]
;
}; @target);
}
.padding-start-end(@start; @end; @target: "") when (default()) {
padding-left: @start;
padding-right: @end;
.enact-locale-rtl({
padding-left: @end;
padding-right: @start;
}, @target);
}
// NOTE: Until we are able to automatically remove these JSDoc-style comments, they should remain LESS-commented
// /**
// * Removes the margin from the appropriate side of the child components that touch the edges of the
// * component this is applied to. This respects both LTR and RTL modes.
// */
.remove-margin-on-edge-children() {
> :first-child {
margin-inline-start: 0;
}
> :last-child {
margin-inline-end: 0;
}
}
// /**
// * Removes the padding from the appropriate side of the child components that touch the edges of the
// * component this is applied to. This respects both LTR and RTL modes.
// */
.remove-padding-on-edge-children() {
> :first-child {
padding-inline-start: 0;
}
> :last-child {
padding-inline-end: 0;
}
}
.full-screen-video-player() {
position: static ;
display: block ;
margin: 0;
}
.hide-full-screen-ancestor() {
position: absolute ;
overflow: visible ;
padding: 0 ;
margin: 0 ;
width: 100% ;
height: 100% ;
}
.input-placeholder(@rule) {
&::placeholder {
@rule();
}
}
// Assign font-kerning rules in a non-proprietary way. Default value being "normal", to enable kerning.
.font-kerning(@val: normal) {
font-kerning: @val;
}
.vendor-fullscreen(@rule) {
&:fullscreen { @rule(); }
}
.vendor-opacity(@opacity) {
opacity: @opacity;
}
// Shorthand for positioning code
.position (@t, @r, @b, @l) {
top: @t;
right: @r;
bottom: @b;
left: @l;
}
.position (@t, @rl, @b) {
.position(@t, @rl, @b, @rl);
}
.position (@tb, @rl) {
.position(@tb, @rl, @tb, @rl);
}
.position (@trbl) when (length(@trbl) = 4) {
.position(
extract(@trbl, 1),
extract(@trbl, 2),
extract(@trbl, 3),
extract(@trbl, 4)
);
}
.position (@trbl) when (length(@trbl) = 3) {
.position(
extract(@trbl, 1),
extract(@trbl, 2),
extract(@trbl, 3)
);
}
.position (@trbl) when (length(@trbl) = 2) {
.position(
extract(@trbl, 1),
extract(@trbl, 2)
);
}
.position (@trbl) when (length(@trbl) = 1) {
.position(@trbl, @trbl, @trbl, @trbl);
}
.border-box() {
box-sizing: border-box;
}
// Helpful debugging way to understand how LESS variables are being interpreted
.debugLessTypes(@value) {
:global(.debugLessTypes) {
value: @value;
isnumber: isnumber(@value);
isstring: isstring(@value);
iscolor: iscolor(@value);
iskeyword: iskeyword(@value);
isurl: isurl(@value);
ispixel: ispixel(@value);
isem: isem(@value);
ispercentage: ispercentage(@value);
isruleset: isruleset(@value);
}
}
//
// Mixin classes supporting advanced text
//
// /**
// * Generate a pair of @font-face rules for a given name and "collection" of locale fonts.
// *
// * This creates two (2) referenceable font-family names, one that "stacks" onto the provided "base"
// * name, the other suffixed with the locale name. E.g. "FontName" and "FontName Locale".
// *
// * Example:
// * ```
// * @fonts: {
// * as: local("LG Smart UI Bengali"); // same as `bn`
// * bn: local("LG Smart UI Bengali");
// * en-JP: local("LG Smart UI JP"), local("LG Display_JP_Bold") 700; // same as `ja`
// * ja: local("LG Smart UI JP"), local("LG Display_JP_Bold") 700; // Generates 2 entries, one for JP set to "normal" (default) weight and another for JP_Bold set to 700 weight
// * ur: local("LG Smart UI_Urdu") normal 300 600 700; // Generates 4 entries, one for each weight
// * };
// * ```
// *
// * List of supported "options" per locale key, in the following order:
// * * single font source - Typically a reference to a local() font or a url() font.
// * * font-weight - Optional, Numbers (100, 500, 700, etc) and keywords (normal, bold, etc) are supported.
// * Empty-string represents "null". This excludes the font-weight rule from the generated code.
// * Multiple weights can be supplied to generate multiple sets of face rules for a single source file
// * * font-style - Optional, Keywords (normal, italic, etc) are supported.
// *
// * OPTIONALLY: repeat the options above after a comma (,) for multiple weights/styles/options based on the same name.
// *
// * @param {String} @baseName The name used as the basis for the generated font-family.
// * @param {Object} @f Object of keys with values detailing the font variants and options.
// */
.buildLocaleFont(@fontName; @locale; @src; @rules) when (isruleset(@rules)) {
.buildFontFace("@{fontName} @{locale}"; @src; @rules);
.buildFontFace(@fontName; @src; @rules);
}
.buildLocaleFont(@fontName; @locale; @args) when (default()) {
@src: extract(@args, 1);
@argLast: extract(@args, length(@args)); // optional style value
@styleExists: boolean((@argLast = italic) or (@argLast = oblique)); // List all valid font-style values here (except for "normal" since that is a shared keyword with font-weight)
@style: if(@styleExists, @argLast);
@lastWeightArgIndex: if(@styleExists, length(@args) - 1, length(@args)); // It's possible this could get confused if "normal" or "bold" is used as the last weight. Please use a number as the last weight.
// Please retain the following comment for future debugging purposes. This gives insight into how the definition values are being interpreted.
// :global(.debug-buildLocaleFont@{fontName}-@{locale}) {
// fontName: @fontName;
// locale: @locale;
// args: @args;
// args-count: length(@args);
// last-weight-arg-index: @lastWeightArgIndex;
// src: @src;
// styleExists: @styleExists;
// style: @style;
// }
// Run when there are 2 or more arguments
each(range(2, @lastWeightArgIndex), .(@argIndex) {
@weight: extract(@args, @argIndex);
.buildFontFace("@{fontName} @{locale}"; @src; @weight; @style);
.buildFontFace(@fontName; @src; @weight; @style);
});
// Run when there are 1 or fewer arguments
& when (length(@args) <= 1) {
// There aren't any additional arguments, so weight and style can be safely omitted from the following calls
.buildFontFace("@{fontName} @{locale}"; @src);
.buildFontFace(@fontName; @src);
}
};
.buildLocaleFonts(@fontName; @f) {
each(@f, .(@specs, @locale) {
// Determine if we're working with a collection of font definitions or an individual definition
@isCollection: if(((length(@specs) > 1) and ((length(extract(@specs, 1)) > 1) or (length(extract(@specs, 2)) > 1))), true, false);
// :global(.debug-buildLocaleFonts-@{locale}) {
// is-collection: @isCollection;
// }
// Interpret collections of font definitions: [(set1) (set2) (set3)]
& when (@isCollection) {
each(@specs, {
.buildLocaleFont(@fontName; @locale; @value);
});
}
// Interpret individual font definitions: (set1)
& when (not(@isCollection)) {
.buildLocaleFont(@fontName; @locale; @specs);
}
// .debugLessTypes(@specs);
// Note: The above `when` code will ignore definitions like the following, because the way
// LESS works, it can't (at this time) differentiate between a space separated list and a
// comma separated list, making it impossible to differentiate a single set of font rules
// with multiple values vs multiple sets with a single value in each.
// {
// nameRepeat: local("name"), local("name2");
// }
// Ex: `local("name") 400` vs `local("name"), local("name2")`
// In this case, only the first value will be used, second, ignored.
});
};
// /**
// * Generate a single @font-face rule.
// *
// * List of supported "options" per locale key, in the following order:
// * @param {String} font-family name The generated font name, referenceable via
// * `font-family: <name>;` in normal CSS.
// * @param {URL} single font source Typically a reference to a local() font or a url() font.
// * @param {Number|Keyword|""} font-weight Optional, Numbers (100, 500, 700, etc) and keywords
// * (normal, bold, etc) are supported. Empty-string represents
// * "null" or default. This excludes the weight rule from the generated code.
// * @param {Keyword} font-style Optional, Keywords (normal, italic, etc) are supported.
// */
.buildFontFace(@name; @src; @rules) when (isruleset(@rules)) {
@font-face {
font-family: @name;
src: @src;
@rules();
}
}
// `length()` below refers to specifying font-weight ranges: `600 900`.
// Ex:
// .buildFontFace("fontName"; local("name"); 400 600);
// -> .buildFontFace("fontName"; local("name"); 400); .buildFontFace("fontName"; local("name"); 600);
// .buildFontFace("fontName"; local("name"); 400 600; normal);
// -> .buildFontFace("fontName"; local("name"); 400; normal); .buildFontFace("fontName"; local("name"); 600; normal);
.buildFontFace(@name; @src; @weight: normal; @style: "") when (length(@weight) > 1) {
each(@weight, {
.buildFontFace(@name; @src; @value; @style);
});
}
// The default version of this mixin (below) is the shorthand version, which rearranges its arguments into the format the above mixin expects.
.buildFontFace(@name; @src; @weight: normal; @style: "") when (default()) {
.buildFontFace(@name; @src; {
// Conditionally add the following
& when ((iskeyword(@weight)) or (isnumber(@weight))) {
font-weight: @weight;
}
& when (iskeyword(@style)) {
font-style: @style;
}
});
}
//
// Locale Mixins
//
// .enact-locale-line-height()
//
// Assign line-height rules specifically to the languages designated
// as needing special support for tall-glyphs.
//
// Set line-height for normal and tallglyphs with 1 list argument
// Ex:
// .enact-locale-line-height(1.4em 1.6em);
// Or:
// @lineheight: 1.4em 1.6em;
// .enact-locale-line-height(@lineheight);
.enact-locale-line-height(@both; @target: normal) when (length(@both) = 2) {
.enact-locale-tallglyph(line-height; @both; @target);
}
// .enact-locale-tallglyph()
//
// Rulename and a normal value followed by a tallglyph value (3 arguments)
// Ex:
// .enact-locale-tallglyph(font-size; 1.4em; 1.6em); -> normal font-size: 1.4em; tallglyphs font-size: 1.6em;
.enact-locale-tallglyph(@rule; @normal; @tallglyph; @target) when (iskeyword(@rule)) {
@{rule}: @normal;
.enact-locale-tallglyph(@rule; @tallglyph; @target);
}
// Rulename and 2 value second variable (2 arguments, 2nd being a list)
// Ex:
// .enact-locale-tallglyph(font-size; 1.4em 1.6em); -> normal font-size: 1.4em; tallglyphs font-size: 1.6em;
// Or:
// @fontsize: 1.4em 1.6em;
// .enact-locale-tallglyph(font-size, @fontsize); -> normal font-size: 1.4em; tallglyphs font-size: 1.6em;
.enact-locale-tallglyph(@rule; @val; @target: normal) when (iskeyword(@rule)) and (length(@val) = 2) {
@{rule}: extract(@val, 1);
.enact-locale-tallglyph(@rule; extract(@val, 2); @target);
}
// Rulename and a tallglyph value
// Ex:
// .enact-locale-tallglyph(font-size; 1.6em); -> tallglyphs font-size: 1.6em;
.enact-locale-tallglyph(@rule; @val; @target: normal) when (iskeyword(@rule)) and (default()) {
.enact-locale-tallglyph({
@{rule}: @val;
}, @target);
}
// Accept a ruleset and apply it to the list of tall-glyph languages
// Ex:
// .enact-locale-tallglyph({
// font-size: 1.6em;
// });
.enact-locale-tallglyph(@rules; @target: normal) when (isruleset(@rules)) and (default()) {
each(@locale-tallglyph-languages, {
// Take each language in the list, apply the @rules to them.
// @value is used to generate an appropriate selector for the @rules.
// Ex: `.enact-locale-th & { @rules(); }`
.enact-locale(@value; @rules; @target);
});
}
// Assign rules specifically to RTL locales with a convenient shorthand.
// Apply these rules to the current selector, not the general parent.
.enact-locale-rtl(@rules; @target) when (isruleset(@rules)) and (@target = self) {
.enact-locale(right-to-left; @rules; @target);
}
// Assign rules specifically to RTL locales with a convenient shorthand.
.enact-locale-rtl(@rules; @target: normal) when (isruleset(@rules)) and (default()) {
.enact-locale(right-to-left; @rules; @target);
}
// Generates an appropriate selector given the locale-target, and injects the given rules into it.
// Apply these rules to the current selector, not the general parent.
.enact-locale(@locale-target; @rules; @target) when (iskeyword(@locale-target)) and (isruleset(@rules)) and (@target = self) {
&:global(.enact-locale-@{locale-target}) {
@rules();
}
}
// Generates an appropriate selector given the locale-target, and injects the given rules into it.
.enact-locale(@locale-target; @rules; @target: normal) when (iskeyword(@locale-target)) and (isruleset(@rules)) and (default()) {
:global(.enact-locale-@{locale-target}) & {
@rules();
}
}
.locale-japanese-line-break() {
.enact-locale(ja; {
line-break: strict;
});
}