diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a4422081d..ca030f09c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,7 +8,7 @@ updates: directory: "/" schedule: interval: "weekly" - day: "friday" + day: "wednesday" time: "00:00" groups: babel: @@ -20,7 +20,7 @@ updates: directory: "/" schedule: interval: "weekly" - day: "friday" + day: "wednesday" time: "00:00" groups: github: diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 000000000..1e2558f89 --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,19 @@ +name: Publish (pre)release to npm + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '10.x' + registry-url: 'https://registry.npmjs.org' + - run: npm publish + env: + # NPM_API_TOKEN is an organization-level secret + NODE_AUTH_TOKEN: ${{ secrets.NPM_API_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 4da679bb8..b050b52d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Changelog is rather internal in nature. See release notes for the public overview and guidelines. Releases are recorded as git tags in the [Github releases](https://github.com/learningequality/kolibri-design-system/releases) page. -## Upcoming version +## Upcoming version 5.x.x (`develop` branch) - [#555] - **Description:** Add action to notify us on Slack about GH issues comments from contributors community @@ -12,8 +12,91 @@ Changelog is rather internal in nature. See release notes for the public overvie - **Breaking:** - - **Impacts a11y:** - - **Guidance:** - + +[#555]: https://github.com/learningequality/kolibri-design-system/pull/555 + +## Version 4.x.x (`release-v4` branch) + +- [#560] + - **Description:** Configure dependabot to run on Wednesday + - **Products impact:** - + - **Addresses:** - + - **Components:** - + - **Breaking:** - + - **Impacts a11y:** - + - **Guidance:** - + +[#560]: https://github.com/learningequality/kolibri-design-system/pull/560 + +- [#558] + - **Description:** Move `useKResponsiveWindow` from `/lib` to `/lib/composables` + - **Products impact:** Location update + - **Addresses:** - + - **Components:** `useKResponsiveWindow` + - **Breaking:** yes + - **Impacts a11y:** - + - **Guidance:** Update `import useKResponsiveWindow from 'kolibri-design-system/lib/useKResponsiveWindow';` from `import useKResponsiveWindow from 'kolibri-design-system/lib/composables/useKResponsiveWindow';` + +[#558]: https://github.com/learningequality/kolibri-design-system/pull/558 + +- [#558] + - **Description:** Remove deprecated `KResponsiveWindow's` mixin documentation page in favor of a new `useKResponsiveWindow` page + - **Products impact:** none + - **Addresses:** - + - **Components:** `KResponsiveWindow` + - **Breaking:** - + - **Impacts a11y:** - + - **Guidance:** - + +[#558]: https://github.com/learningequality/kolibri-design-system/pull/558 + +- [#558] + - **Description:** Adds engines and browserlist to package.json. Pins dependencies to exact version. + - **Products impact:** Dependencies + - **Addresses:** - + - **Components:** - + - **Breaking:** - + - **Impacts a11y:** - + - **Guidance:** - + +[#558]: https://github.com/learningequality/kolibri-design-system/pull/558 + +- [#558] + - **Description:** Internal maintenance tasks: extract common logic to utils, move private composables to `/lib/composables` and indicate that they are private by `_` prefix in their filename. dev docs updates. + - **Products impact:** none + - **Addresses:** - + - **Components:** - + - **Breaking:** - + - **Impacts a11y:** - + - **Guidance:** - +<<<<<<< HEAD [#555]: https://github.com/learningequality/kolibri-design-system/pull/555 +======= + +[#558]: https://github.com/learningequality/kolibri-design-system/pull/558 + +- [#551] + - **Description:** Updates `brand` colors, `palette` colors, and `token`s. + - Breaking changes: + - Removed `palette` colors: `purple`, `deeppurple`, `indigo`, `brown`, `cyan`, `teal`, `lightgreen`, `lime`, `amber`, `deeporange`, `bluegrey` + - Removed `palette.grey` scales: `v_300`, `v_500`, `v_700`, `v_900` + - Removed `brand` and `palette` scales (except `palette.grey`): `v_50`,`v_100`, `v_300`, `v_500`, `v_700`, `v_900` + - Removed content-related tokens: `exercise`, `video`, `audio`, `document`, `html5`, `slideshow` + - Removed other tokens: `appBarFullscreen`, `appBarFullscreenDark`, `linkDark` + - Other changes: + - Some existing `palette` colors look differently + - Adds new tokens and palette + - Global styles: `` background color changed from `grey.v_100` to lighter `grey.v_50` + - **Products impact:** new API, updated API, deleted API + - **Addresses:** - https://github.com/learningequality/kolibri-design-system/issues/545 + - **Components:** - + - **Breaking:** - yes + - **Impacts a11y:** - no + - **Guidance:** - Address all breaking changes by searching for removed palette colors, scales, and tokens. Study the updated "Colors" KDS documentation page and replace them by relevant colors/scales/tokens. Also search for any hardcoded hex,rgb(a),hsl(a), or named colors (such as 'white') and theme them instead. Visually check places that use existing palette and adjust scale (you may need to increase it as many colors are lighter). You may also see if there are any minor useful updates to in regards to new tokens (e.g. replacing a `palette` color with a new `token` that describes function of the color better). If you use `generateGlobalStyles` that generates background color for `` and use grey.`v_100` in some components to match the background color, you may need to update it to `grey.v_50`. + +[#551]: https://github.com/learningequality/kolibri-design-system/pull/551 +>>>>>>> develop - [#531] - **Description:** Remove unused `keen-ui` dependency @@ -26,7 +109,106 @@ Changelog is rather internal in nature. See release notes for the public overvie [#531]: https://github.com/learningequality/kolibri-design-system/pull/531 -## Version 3.0.0 +## Version 3.x.x (`release-v3` branch) + +- [#552] + - **Description:** New `KListWithOverflow` component. + - **Products impact:** new API. + - **Addresses:** https://github.com/learningequality/kolibri-design-system/issues/556, https://github.com/learningequality/studio/issues/3423, https://github.com/learningequality/kolibri/issues/11923. + - **Components:** KListWithOverflow. + - **Breaking:** no. + - **Impacts a11y:** no. + - **Guidance:** -. + +[#552]: https://github.com/learningequality/kolibri-design-system/pull/552 + +- [#552] + - **Description:** New `useKResponsiveElement` private composable, `KResponsiveElementMixin` translated to this composable. + - **Products impact:** -. + - **Addresses:** -. + - **Components:** -. + - **Breaking:** no. + - **Impacts a11y:** no. + - **Guidance:** -. + +[#552]: https://github.com/learningequality/kolibri-design-system/pull/552 + +- [#538] + - **Description:** Complete KImg implementation + - **Products impact:** new API + - **Addresses:** https://github.com/learningequality/kolibri-design-system/issues/368 + - **Components:** KImg + - **Breaking:** no + - **Impacts a11y:** yes + - **Guidance:** One of the benefits of using KImg is that it throws a11y related warnings + +[#538]: https://github.com/learningequality/kolibri-design-system/pull/538 + +- [#557] + - **Description:** Updates development documentation in regards to linking products development servers to local KDS + - **Products impact:** - + - **Addresses:** - + - **Components:** - + - **Breaking:** - + - **Impacts a11y:** - + - **Guidance:** - + +[#557]: https://github.com/learningequality/kolibri-design-system/pull/557 + +- [#542] + - **Description:** Adds a new `sort` icon + - **Products impact:** New icon + - **Addresses:** https://github.com/learningequality/studio/issues/4426 + - **Components:** - + - **Breaking:** no + - **Impacts a11y:** - + - **Guidance:** - + +[#542]: https://github.com/learningequality/kolibri-design-system/pull/542 + +- [#542] + - **Description:** Updates documentation for icons to the new process, adds clear guidelines + - **Products impact:** None + - **Addresses:** - + - **Components:** - + - **Breaking:** - + - **Impacts a11y:** - + - **Guidance:** - + +[#542]: (https://github.com/learningequality/kolibri-design-system/pull/542 + +- [#543] + - **Description:** Added new Icons to support Studio Usability Enhancements + - **Products impact:** new API + - **Addresses:** https://github.com/learningequality/studio/issues/3425 + - **Components:** KIcon + - **Breaking:** no + - **Impacts a11y:** no + - **Guidance:** Consumers can now access these icons: activities, attribution, audience, categories, levels, rename + +[#543]: https://github.com/learningequality/kolibri-design-system/pull/543 + +- [#541] + - **Description:** Add a GitHub Actions workflow to publish a new release on npm + - **Products impact:** none + - **Addresses:** https://github.com/learningequality/kolibri-design-system/issues/532 + - **Components:** - + - **Breaking:** - + - **Impacts a11y:** - + - **Guidance:** - + +[#541]: https://github.com/learningequality/kolibri-design-system/pull/541 + +- [#535] + - **Description:** Added text prop in the KToolTip component as an alternative to the slot + - **Products impact:** Choose from - bugfix + - **Addresses:** #221 + - **Components:** KToolTip + - **Breaking:** no + - **Impacts a11y:** no + - **Guidance:** - + +[#535]: https://github.com/learningequality/kolibri-design-system/pull/535 - [#522] - **Description:** Upgrades github-actions/cache dependency diff --git a/README.md b/README.md index 0b119ee2e..462376f2c 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,7 @@ For the latest (not yet released) version, visit the design system website built ### Component library -The component library is a node package hosted on GitHub. It contains front-end components, utilities, and style definitions supporting the Kolibri Design System and used in Kolibri products. - -To add a particular pinned version to a project using `yarn`, for example `v1.0.1`, run: - -```bash -yarn add https://github.com/learningequality/kolibri-design-system#v1.0.1 -``` +The component library is [a npm package](https://www.npmjs.com/package/kolibri-design-system). It contains front-end components, utilities, and style definitions supporting the Kolibri Design System and used in Kolibri products. Components and utilities will be accessible under the `lib` path. For example: diff --git a/custom-icons/activities.svg b/custom-icons/activities.svg new file mode 100644 index 000000000..40e480836 --- /dev/null +++ b/custom-icons/activities.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/custom-icons/categories.svg b/custom-icons/categories.svg new file mode 100644 index 000000000..4383b2762 --- /dev/null +++ b/custom-icons/categories.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom-icons/levels.svg b/custom-icons/levels.svg new file mode 100644 index 000000000..c6556c8df --- /dev/null +++ b/custom-icons/levels.svg @@ -0,0 +1,3 @@ + + + diff --git a/custom-icons/rename.svg b/custom-icons/rename.svg new file mode 100644 index 000000000..f6c6fad18 --- /dev/null +++ b/custom-icons/rename.svg @@ -0,0 +1,3 @@ + + + diff --git a/custom-icons/sort.svg b/custom-icons/sort.svg new file mode 100644 index 000000000..ff53467a9 --- /dev/null +++ b/custom-icons/sort.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dev_docs/03_how_to_update_library.md b/dev_docs/03_how_to_update_library.md index f7b19d28a..f76f7b65d 100644 --- a/dev_docs/03_how_to_update_library.md +++ b/dev_docs/03_how_to_update_library.md @@ -51,14 +51,18 @@ When creating a new component, it is recommended to add a new documentation page ### (2) Preview updates in a product -To see local Kolibri Design System updates reflected in a product that is using it (such as [Kolibri Learning Platform](https://github.com/learningequality/kolibri) or [Kolibri Studio](https://github.com/learningequality/studio)): +You can test local Kolibri Design System updates reflected in a product that is using it, such as [Kolibri Learning Platform](https://github.com/learningequality/kolibri) or [Kolibri Studio](https://github.com/learningequality/studio). + +For Kolibri Learning Platform, we recommend `devserver-with-kds` command. See ["Running Kolibri with local Kolibri Design System"](https://kolibri-dev.readthedocs.io/en/develop/howtos/development_with_kds.html) guide. + +In other products, you can use `yarn link`: 1. While in the root of your local `kolibri-design-system` repository, run `yarn link`. -2. In the root of the product where you intend to use `kolibri-design-system` run `yarn link kolibri-design-system` and then `yarn install`. +2. In the root of a product where you intend to use `kolibri-design-system` run `yarn link kolibri-design-system` and then `yarn install`. Now, when you run the product your changes in `kolibri-design-system` will be updated live when running the product's development server. -For example, to test Kolibri Design System in Kolibri Learning Platform (local `kolibri` repository): +For example, to test Kolibri Design System in Kolibri Studio (local `studio` repository): ```bash # change to the Kolibri Design System repository and add it to yarn's local package registry @@ -66,16 +70,14 @@ cd ./kolibri-design-system yarn link # change to the Kolibri repository and link it to the local Kolibri Design System package -cd ../kolibri +cd ../studio yarn link kolibri-design-system -# re-install Kolibri dependencies +# re-install Studio dependencies yarn install -# run the Kolibri development server +# run the Studio development server yarn run devserver ``` -Note that to be able to run the Kolibri development server, at first you need to have it set up as described in [Kolibri developer documentation](https://kolibri-dev.readthedocs.io/en/develop/getting_started.html). - -Now you're all set to see your changes to the Kolibri Design System working live in Kolibri! +Now you're all set to see your changes to the Kolibri Design System working live in Studio! diff --git a/dev_docs/05_icons.md b/dev_docs/05_icons.md index 4953a1bcf..0415ecb2b 100644 --- a/dev_docs/05_icons.md +++ b/dev_docs/05_icons.md @@ -1,48 +1,35 @@ # Icons -This page is all about Kolibri Design System icons. It includes information on our icon sources and provides guidelines on related workflows. +Here, you can find all about icons - where to get them, how to add them, etc. -## Sources and regenerating Vue components +## Introduction -There are three sources of icons: +Source icon files are stored as svg files in [`custom-icons`](../custom-icons/). -- [Google Material Design Icons](https://github.com/material-icons/material-icons) (version pinned in [yarn.lock](yarn.lock)) -- [Material Design Icons](https://github.com/Templarian/MaterialDesign-SVG) (version pinned in [yarn.lock](yarn.lock)) -- [Custom Learning Equality icons]([custom-icons](../custom-icons/)) +### Icons as Vue components -Icons from these sources are then converted to Vue components by our custom precompilation script. After updating any of these sources, we need to regenerate the Vue components by running: +Source svg files are converted to Vue components by our precompilation script. The resulting Vue components are made public via [the icons definitions file `lib/KIcon/iconDefinitions.js`](../lib/KIcon/iconDefinitions.js). The documentation website contains automatically generated [list of available icons](https://design-system.learningequality.org/icons/#icons) that can be used via Vue components, such as `KIcon`. -```bash -yarn run precompile-svgs -``` +### Icons for reStructuredText -You can also regenerate just the custom icons which is faster: +In order to use icons on some Learning Equality's documentation pages written in reStructuredText, we also have a script to generate a set of reStructuredText replacement strings to [`docs/rstIconReplacements.txt`](../docs/rstIconReplacements.txt) which can be used in docs based on Sphinx. The file is available for download from https://design-system.learningequality.org/icons/#rst. -```bash -yarn run precompile-custom-svgs -``` +## How to add a new icon -One of these commands should be run after any icon changes. +1. **Paste a svg file** to [`custom-icons`](../custom-icons/) +2. **Run `yarn run precompile-custom-svgs` to generate a corresponding Vue component.** The generated component will be saved to [`lib/KIcon/precompiled-icons/le`](../lib/KIcon/precompiled-icons/le) +3. **Add a new entry for the generated component to [the icons definitions file](../lib/KIcon/iconDefinitions.js).** As a key name, choose a unique alias that describes the purpose of the icon in our products well. Set the following attributes: + - `icon`: A Vue component which renders the icon (generated in the previous step) + - (optional) `rtlFlip`: When `true`, the icon will be flipped for right-to-left lanauges + - (optional) `defaultColor`: A color for the icon. If not defined, icons are `themeTokens.text` colored + - (optional) `fixedColor`: When `true`, the icon has a fixed color and `KIcon`'s `color` prop will be ignored +4. **Run `yarn run pregenerate` to update icons for reStructuredText documents.** Note that it's important to run this step **after** updating the icons definitions file, otherwise it won't be detected. -We don't expose all icons in our KDS public API. Only icons defined in [the icons definitions file](lib/KIcon/iconDefinitions.js) are exposed, and we use our custom aliases for them. +To **check that the new icon is successfully added**: +- Run the development server +- See that the new icon is present in the icons list: http://localhost:4000/icons#icons +- See that there is a new entry for the icon in the reStructuredText replacement strings file: http://localhost:4000/icons#rst -In order to use icons in documentation we also output a set of reStructuredText replacement strings. These are added the file `docs/rstIconReplacements.txt` which can be used in docs based on Sphinx. The file is available for download from https://design-system.learningequality.org/icons/#rst +## How to update an existing icon -This command should be run after any icon ID changes. - -```bash -yarn run pregenerate -``` - -### Example: Upgrading Google Material Design Icons - -It is advised to commit changes at each step to make reviewing files other than those in *precompiled-icons/* easier, especially in case of large updates. - -1. Run `yarn upgrade @material-icons/svg` -2. Run `yarn run precompile-svgs` -3. Review updates of all public icons defined in [the icons definitions file](lib/KIcon/iconDefinitions.js) - -Large upgrades can result in a colossal git diff which makes reviewing changes of selected public icons in detail difficult. To make such upgrades smoother, in addition to visually reviewing [icons in KDS documentation](https://design-system.learningequality.org/icons/#icons), you can use a report that is printed in a terminal as soon as the precompilation process ends. It contains all exposed icons aliases together with information about whether an icon has been updated or no. If it's been updated, git diff will be printed. - -4. Run `yarn run pregenerate` -5. Write down notes to the changelog about any public updates like visual changes of icons, updates of their aliases, and updates of reStructuredText replacement strings +The update process is similar to creating a new icon. After every update, run `yarn run precompile-custom-svgs` and `yarn run pregenerate`. diff --git a/docs/assets/hummingbird CC BY-SA 4.0.jpg b/docs/assets/hummingbird CC BY-SA 4.0.jpg new file mode 100644 index 000000000..967146ca4 Binary files /dev/null and b/docs/assets/hummingbird CC BY-SA 4.0.jpg differ diff --git a/docs/assets/img_sample_for_kimg.png b/docs/assets/img_sample_for_kimg.png deleted file mode 100644 index f06c72c46..000000000 Binary files a/docs/assets/img_sample_for_kimg.png and /dev/null differ diff --git a/docs/pages/colors.vue b/docs/pages/colors.vue index db6ecc511..312829331 100644 --- a/docs/pages/colors.vue +++ b/docs/pages/colors.vue @@ -17,8 +17,8 @@ - - + +

@@ -117,11 +117,37 @@ Default dark variant of the primary brand color. Commonly used for the hover state of interactive elements + + Default secondary brand color + + + Default dark variant of the secondary brand color +

UI Colors

Default background color + + Default app bar component color + + + Default dark color of the app bar component + + + Hyperlink text color with the application + + + Used for divider lines and rules + + + Color for loaders, spinners, and other progress indicators + + + Indicates the successful completion of an action in the application + + +

Text

Normal text color. (Typically used on top of the $themeTokens.surface color) @@ -134,21 +160,12 @@ Text color for creating sufficient contrast when used on dark backgrounds (such as $themeTokens.primary) - - Color for loaders, spinners, and other progress indicators - Used to indicate keyboard focus - - Used for divider lines and rules - Indicates an application or validation error - - Indicates the successful completion of an action in the application -

Learner activity

@@ -173,27 +190,29 @@

Content-related labels

- - - - - + + + + + + +

- A color scale – sometimes called a color ramp – is an evenly-spaced ramp of shades for a particular color hue. In the Kolibri Design System, we follow and segment colors into ten brightness levels, named v_50, v_100, v_20, … v_800, v_900: + A color scale – sometimes called a color ramp – is an evenly-spaced ramp of shades for a particular color hue. In the Kolibri Design System, we follow and segment colors into brightness levels, named v_200, v_400, v_600, … v_1000, v_1100:

- - - + + +

- - + +

Due to the inconsistent way that humans perceive color and light, computing these scales is . It should not be done by simply sliding a "brightness" setting. We used to generate the scales for our primary and secondary brand colors. The same should be done for new themes.

@@ -203,9 +222,6 @@

Brand colors are chosen to reflect the mood, identity, or trademark of an application or an organization. The design system defines primary (dominant) and secondary (accent) branded color hues.

-

- The Kolibri Design System has what we call "Kolibri Purple" as its primary and "Kolibri Teal" as its secondary, available across the full brightness scale: -

diff --git a/docs/pages/kcircularloader.vue b/docs/pages/kcircularloader.vue index 607c97319..bf6549b5a 100644 --- a/docs/pages/kcircularloader.vue +++ b/docs/pages/kcircularloader.vue @@ -17,7 +17,7 @@
  • Dimensions do not exceed 48x48px
  • -
  • Progress indicator uses brand.secondary.v_100
  • +
  • Progress indicator uses palette.grey.v_800
  • Track width is 4px
diff --git a/docs/pages/kimg.vue b/docs/pages/kimg.vue index a28103f6d..487594a1c 100644 --- a/docs/pages/kimg.vue +++ b/docs/pages/kimg.vue @@ -2,117 +2,335 @@ -
- The component displays images based on an image source and optional image - dimensions provided by the implementer. +

KImg displays an image and provides functionality to manipulate it such as setting its dimensions, aspect ratio, scaling, letterboxing, and more.

+ + + +

Unless you set fixed dimensions, KImg is responsive by default.

+ +

+ Note that the majority of settings are applied to the image container rather than the image itself. If you need to apply dimensions, aspect ratio, etc. directly to the image, you can use 'fitXY' + scale type + . +

+ +

+ Depending on the scale type and other settings, the image may be letterboxed. When an image is letterboxed, + backgroundColor + (gray by default) fills the remaining space. +

+ +

This is in more detail illustrated in the examples below, where the original image dimensions are 200×114 px.

+ +

Rendering within inline and block elements

+ +

Inline

+ +

When rendered within an inline element, the image preserves its original dimensions by default.

+ + + + + + + + + + + + -

Sample implementations of including props:

+

Block

+

+ When rendered within a block element, the image container fills the parent block element and the image scales with the 'centerInside' + scale type + by default. +

+ +
- Dimensions may be either numbers or strings consisting of a numeral and valid units (e.g. px, - em, vh). +
+
+ +
+ +
+
+ +

Dimensions

-
- - - - - - +

+ You can apply the most common dimensions to the image container via props such as + width + , + maxHeight + , and others. Values may be either numbers or strings consisting of a numeral and a valid unit. The following units are supported: %, cm, em, ex, ch, in, lh, mm, px, rem, rlh, vh, vw. If you don't provide a unit, px will be used by default. +

+ + + + + +

Alternative text

+ +

+ Unless an image is , you need to provide alternative text via + altText + . +

+ + + + + +

+ If it's meant to be decorative, indicate it by using + isDecorative + . Alternative text won't be required in this case and the image will be hidden from assistive technologies. +

+ + + + + +

Scaling

+ +

+ The + scale type + determines how an image scales within the image container. +

+ +

'centerInside' scale type

+ +

Scales an image uniformly and maintains its original aspect ratio. Both its width and height are equal to or less than the width and height of the image container. An image can be letterboxed. It's the safest mode as it never distorts an image or impairs its quality.

+ + +
+
-
+ + +
+ +
+
+ +

'contain' scale type

+ +

Behaves similarly to 'centerInside' except it ensures that at least one axis of an image fits the image container exactly. The original aspect ratio is maintained. An image can be letterboxed. This mode may impair an image's quality by enlarging it above its original size.

-
- requires alternative text that describes the image unless - isDecorative is true. In that case, any alt text provided will be overwritten to an - empty string. + +
+ +
+
+
-
- - - -
- - - +
-
+ + +

'fitXY' scale type

+ +

Scales X and Y axis of an image independently, so that the image matches the container exactly. An image is never letterboxed. This mode may impair an image's quality by enlarging it above its original size, or distort its aspect ratio.

-
- If dimensions for the image are not specified, the size will default to the height and width of the source - image. - + +
- - +
+
+ +
- -
- -
- You can also use the - appearanceOverrides prop - to add custom styles to the image. It accepts any Vue dynamic styles object and applies the same with the highest precedence. - +
+
+ +

Aspect ratio

+ +

+ You can set the aspect ratio of the image container with + + aspectRatio + and combine it with any of the scale types. +

+ +

Note that the ratio styles calculations need to have the width information, therefore it needs to be available in some way. For example, you can set the width directly on KImg. Alternatively, you could ensure that KImg's parent element has width by setting it explicitly or by using a block element.

+ + +
- - +
+
+ +
- - Even thought the width prop specifies the width of the image to be 100%, the appearanceOverrides prop overrides it and sets the width to 50%. -
+
+ + +

Placeholder area

+ +

+ The placeholder area is displayed when an image is not available. The area respects the dimensions set on the image container and is gray by default. You can change the area color via + backgroundColor + and use the + #placeholder + + slot to place content in the area. +

+ + +
+ + + +
+
+ +
+ + + +
+
+ +

Displaying content on top of an image

+ +

+ Use + #topLeft + , + #topRight + , + #bottomLeft + , or + #bottomRight + slots to place content on top of the image container. +

+ + + + + + + + + + + + +
+ @@ -127,27 +345,9 @@ - - - - - diff --git a/docs/pages/klinearloader.vue b/docs/pages/klinearloader.vue index f4eedcdf4..ecd0af5da 100644 --- a/docs/pages/klinearloader.vue +++ b/docs/pages/klinearloader.vue @@ -36,8 +36,8 @@
    -
  • Progress track uses palette.grey.v_200
  • -
  • Progress indicator uses brand.secondary.v_100
  • +
  • Progress track uses palette.grey.v_100
  • +
  • Progress indicator uses palette.grey.v_800
  • Progress track height is 4px
  • Include a 2px corner radius
  • Remove the corner radius when appended to other components, such as appbars or data tables
  • diff --git a/docs/pages/klistwithoverflow.vue b/docs/pages/klistwithoverflow.vue new file mode 100644 index 000000000..495321f6b --- /dev/null +++ b/docs/pages/klistwithoverflow.vue @@ -0,0 +1,249 @@ + + + + + + + diff --git a/docs/pages/kresponsivewindow.vue b/docs/pages/kresponsivewindow.vue deleted file mode 100644 index a47985dfe..000000000 --- a/docs/pages/kresponsivewindow.vue +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - diff --git a/docs/pages/kselect.vue b/docs/pages/kselect.vue index 65b68bfd9..63d0c53ba 100644 --- a/docs/pages/kselect.vue +++ b/docs/pages/kselect.vue @@ -9,6 +9,7 @@ :options="options" label="Who are you?" placeholder="Nobody" + style="width:300px;" />

    Value: {{ exampleData }}

    diff --git a/docs/pages/kswitch.vue b/docs/pages/kswitch.vue index e00313b63..7787bf918 100644 --- a/docs/pages/kswitch.vue +++ b/docs/pages/kswitch.vue @@ -33,19 +33,19 @@
    • Thumb ON color: - +
    • Track ON color: - +
    • Thumb OFF color: - +
    • Track OFF color: - +
    diff --git a/docs/pages/menus/index.vue b/docs/pages/menus/index.vue index c6d5f1597..ae865e0b1 100644 --- a/docs/pages/menus/index.vue +++ b/docs/pages/menus/index.vue @@ -99,7 +99,7 @@
  • Iconography color: - +

Dropdown menu

diff --git a/docs/pages/textfields/index.vue b/docs/pages/textfields/index.vue index 07f40cf7e..6a758cf0d 100644 --- a/docs/pages/textfields/index.vue +++ b/docs/pages/textfields/index.vue @@ -184,11 +184,11 @@
  • Corner radius: 2px
  • Fill color: - , 0.25 opacity +
  • Bottom stroke color: - +
  • Bottom stroke focused color: @@ -196,7 +196,7 @@
  • Label text color: - +
  • Focused text color: diff --git a/docs/pages/usekresponsivewindow.vue b/docs/pages/usekresponsivewindow.vue new file mode 100644 index 000000000..fb86a7681 --- /dev/null +++ b/docs/pages/usekresponsivewindow.vue @@ -0,0 +1,145 @@ + + + + + + + diff --git a/docs/rstIconReplacements.txt b/docs/rstIconReplacements.txt index 52b12a1de..c4c9dafa4 100644 --- a/docs/rstIconReplacements.txt +++ b/docs/rstIconReplacements.txt @@ -1,14 +1,18 @@ .. |a11y| replace:: :raw-html:`` +.. |activities| replace:: :raw-html:`` .. |add| replace:: :raw-html:`` .. |allActivities| replace:: :raw-html:`` .. |alternativeRoute| replace:: :raw-html:`` .. |app| replace:: :raw-html:`` .. |artsResource| replace:: :raw-html:`` +.. |attribution| replace:: :raw-html:`` +.. |audience| replace:: :raw-html:`` .. |audio| replace:: :raw-html:`` .. |back| replace:: :raw-html:`` .. |bookmarkEmpty| replace:: :raw-html:`` .. |bookmark| replace:: :raw-html:`` .. |brokenImage| replace:: :raw-html:`` +.. |categories| replace:: :raw-html:`` .. |channel| replace:: :raw-html:`` .. |checked| replace:: :raw-html:`` .. |check| replace:: :raw-html:`` @@ -89,6 +93,7 @@ .. |learn| replace:: :raw-html:`` .. |lessonPlansResource| replace:: :raw-html:`` .. |lesson| replace:: :raw-html:`` +.. |levels| replace:: :raw-html:`` .. |library| replace:: :raw-html:`` .. |listenShaded| replace:: :raw-html:`` .. |listenSolid| replace:: :raw-html:`` @@ -143,6 +148,7 @@ .. |registeredKDP| replace:: :raw-html:`` .. |registered| replace:: :raw-html:`` .. |remove| replace:: :raw-html:`` +.. |rename| replace:: :raw-html:`` .. |reports| replace:: :raw-html:`` .. |resourceList| replace:: :raw-html:`` .. |restart| replace:: :raw-html:`` @@ -155,6 +161,7 @@ .. |skillsResource| replace:: :raw-html:`` .. |slideshow| replace:: :raw-html:`` .. |socialSciencesResource| replace:: :raw-html:`` +.. |sort| replace:: :raw-html:`` .. |starBorder| replace:: :raw-html:`` .. |star| replace:: :raw-html:`` .. |superadmin| replace:: :raw-html:`` diff --git a/docs/tableOfContents.js b/docs/tableOfContents.js index 55d4d5670..c6aa32e34 100644 --- a/docs/tableOfContents.js +++ b/docs/tableOfContents.js @@ -58,7 +58,6 @@ class Page { const buttonRelatedKeywords = ['button', 'link']; const textRelatedKeywords = ['text', 'area', 'field', 'box']; const layoutRelatedKeywords = ['grid', 'layout', 'container', 'page']; -const responsiveComponentsRelatedKeywords = ['responsive', 'mixin', 'breakpoint']; const tabsRelatedKeywords = ['tab', 'tabs', 'panel', 'tablist', 'tabpanel']; const compositionRelatedKeywords = ['composable', 'composition']; @@ -175,6 +174,12 @@ export default [ title: 'Composables', autoSort: true, pages: [ + new Page({ + path: '/usekresponsivewindow', + title: 'useKResponsiveWindow', + isCode: true, + keywords: [...compositionRelatedKeywords, 'responsive', 'window', 'breakpoint'], + }), new Page({ path: '/usekshow', title: 'useKShow', @@ -316,11 +321,6 @@ export default [ isCode: true, keywords: ['button'], }), - new Page({ - path: '/kcontentrenderer', - title: 'KContentRenderer', - isCode: true, - }), new Page({ path: '/kgrid', title: 'KGrid', @@ -356,17 +356,11 @@ export default [ title: 'KBreadcrumbs', isCode: true, }), - new Page({ - path: '/kresponsivewindow', - title: 'KResponsiveWindow', - isCode: true, - keywords: [...responsiveComponentsRelatedKeywords, 'window'], - }), new Page({ path: '/kresponsiveelement', title: 'KResponsiveElement', isCode: true, - keywords: [...responsiveComponentsRelatedKeywords, 'element'], + keywords: ['responsive', 'mixin', 'breakpoint', 'element'], }), new Page({ path: '/ktabs', @@ -397,6 +391,11 @@ export default [ title: 'KTextTruncator', isCode: true, }), + new Page({ + path: '/klistwithoverflow', + title: 'KListWithOverflow', + isCode: true, + }), ], }), ]; diff --git a/lib/KDateRange/KDateCalendar.vue b/lib/KDateRange/KDateCalendar.vue index e67d57fe3..f9c1869d2 100644 --- a/lib/KDateRange/KDateCalendar.vue +++ b/lib/KDateRange/KDateCalendar.vue @@ -31,7 +31,7 @@ :style="[ (selectionOrder(weekIndex, dayInWeekIndex, 'first', activeMonthDay, activeMonthDate) === 'first') || (selectionOrder(weekIndex, dayInWeekIndex, 'first', activeMonthDay, activeMonthDate) === 'second') ? - { backgroundColor: $themeBrand.secondary.v_50 } : {} + { backgroundColor: $themeBrand.primary.v_200 } : {} ]" :class="[{ 'calendar-days--disabled': isDateDisabled(weekIndex, dayInWeekIndex, activeMonthDay, activeMonthDate) || isDateDisabledLeft(weekIndex, dayInWeekIndex, activeMonthDay), @@ -64,7 +64,7 @@ :style="[ (selectionOrder(weekIndex, dayInWeekIndex, 'second', nextActiveMonthDay, nextActiveMonthDate) === 'first') || (selectionOrder(weekIndex, dayInWeekIndex, 'second', nextActiveMonthDay, nextActiveMonthDate) === 'second') ? - { backgroundColor: $themeBrand.secondary.v_50 } : {} + { backgroundColor: $themeBrand.primary.v_200 } : {} ]" :class="[{ 'calendar-days--disabled': isDateDisabled(weekIndex, dayInWeekIndex, nextActiveMonthDay, nextActiveMonthDate) || isDateDisabledRight(weekIndex, dayInWeekIndex, nextActiveMonthDay), diff --git a/lib/KDateRange/KDateDay.vue b/lib/KDateRange/KDateDay.vue index a32b23ea0..a968e287d 100644 --- a/lib/KDateRange/KDateDay.vue +++ b/lib/KDateRange/KDateDay.vue @@ -69,7 +69,7 @@ inRangeStyle() { return this.isInRange ? { - backgroundColor: this.$themeBrand.secondary.v_50, + backgroundColor: this.$themeBrand.primary.v_200, ':hover': { backgroundColor: this.$themePalette.grey.v_200, }, @@ -79,7 +79,7 @@ selectedStyle() { return this.isSelected ? { - backgroundColor: this.$themeBrand.secondary.v_600, + backgroundColor: this.$themeBrand.primary.v_1000, color: this.$themePalette.white + '!important', } : {}; diff --git a/lib/KIcon/iconDefinitions.js b/lib/KIcon/iconDefinitions.js index 1c64312ca..1c05266e7 100644 --- a/lib/KIcon/iconDefinitions.js +++ b/lib/KIcon/iconDefinitions.js @@ -65,6 +65,7 @@ const KolibriIcons = { icon: require('./precompiled-icons/material-icons/broken_image/baseline.vue').default, defaultColor: themePalette().grey.v_400, }, + sort: { icon: require('./precompiled-icons/le/sort.vue').default }, // Features and links learn: { icon: require('./precompiled-icons/material-icons/school/baseline.vue').default }, @@ -81,6 +82,7 @@ const KolibriIcons = { people: { icon: require('./precompiled-icons/material-icons/people/baseline.vue').default }, person: { icon: require('./precompiled-icons/material-icons/person/baseline.vue').default }, permission: { icon: require('./precompiled-icons/material-icons/vpn_key/baseline.vue').default }, + audience: { icon: require('./precompiled-icons/material-icons/groups/baseline.vue').default }, // for use in RST docs as we cannot change the fill dynamically in RST superadmin: { icon: require('./precompiled-icons/material-icons/vpn_key/baseline.vue').default, @@ -219,6 +221,10 @@ const KolibriIcons = { check: { icon: require('./precompiled-icons/material-icons/check/baseline.vue').default }, help: { icon: require('./precompiled-icons/material-icons/help_outline/outline.vue').default }, close: { icon: require('./precompiled-icons/material-icons/close/baseline.vue').default }, + rename: { + icon: require('./precompiled-icons/le/rename.vue').default, + rtlFlip: true, + }, // Found while removing mat-svg indeterminateCheck: { @@ -297,6 +303,17 @@ const KolibriIcons = { notPinned: { icon: require('./precompiled-icons/material-icons/push_pin/outline.vue').default, }, + // Content node fields + categories: { icon: require('./precompiled-icons/le/categories.vue').default }, + activities: { icon: require('./precompiled-icons/le/activities.vue').default }, + levels: { + icon: require('./precompiled-icons/le/levels.vue').default, + rtlFlip: true, + }, + attribution: { + icon: require('./precompiled-icons/material-icons/attribution/baseline.vue').default, + }, + // for use in RST docs as we cannot change the fill dynamically in RST registeredKDP: { icon: require('./precompiled-icons/material-icons/verified_user/baseline.vue').default, diff --git a/lib/KIcon/precompiled-icons/le/activities.vue b/lib/KIcon/precompiled-icons/le/activities.vue new file mode 100644 index 000000000..e5773f968 --- /dev/null +++ b/lib/KIcon/precompiled-icons/le/activities.vue @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/lib/KIcon/precompiled-icons/le/categories.vue b/lib/KIcon/precompiled-icons/le/categories.vue new file mode 100644 index 000000000..e50343f76 --- /dev/null +++ b/lib/KIcon/precompiled-icons/le/categories.vue @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/lib/KIcon/precompiled-icons/le/levels.vue b/lib/KIcon/precompiled-icons/le/levels.vue new file mode 100644 index 000000000..647fa1b5b --- /dev/null +++ b/lib/KIcon/precompiled-icons/le/levels.vue @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/lib/KIcon/precompiled-icons/le/rename.vue b/lib/KIcon/precompiled-icons/le/rename.vue new file mode 100644 index 000000000..67db183cf --- /dev/null +++ b/lib/KIcon/precompiled-icons/le/rename.vue @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/lib/KIcon/precompiled-icons/le/sort.vue b/lib/KIcon/precompiled-icons/le/sort.vue new file mode 100644 index 000000000..fb1da67d2 --- /dev/null +++ b/lib/KIcon/precompiled-icons/le/sort.vue @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/lib/KImg/__tests__/KImg.spec.js b/lib/KImg/__tests__/KImg.spec.js index 3efb4dcd7..f318e493a 100644 --- a/lib/KImg/__tests__/KImg.spec.js +++ b/lib/KImg/__tests__/KImg.spec.js @@ -1,45 +1,69 @@ -import { mount } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; import KImg from '../'; -describe('KImg component', () => { - let wrapper; - let img; +function makeWrapper({ propsData = {} } = {}) { + return shallowMount(KImg, { propsData }); +} - const DEFAULT_PROPS = { - src: 'https://learningequality.org/static/img/le-logo.svg', - altText: "Learning Equality's logo", - }; - - function makeWrapper(newProps = {}) { - wrapper = mount(KImg, { - propsData: { ...DEFAULT_PROPS, ...newProps }, +describe('KImg', () => { + it(`renders without any errors when a valid 'src' and 'altText' are provided`, () => { + const wrapper = makeWrapper({ + propsData: { src: '/le-logo.svg', altText: 'LE logo' }, }); - img = wrapper.find('img'); - } - it('Renders without any errors when a valid src and altText are provided', () => { - makeWrapper(); expect(wrapper.exists()).toBe(true); + + const img = wrapper.find('img'); expect(img.exists()).toBe(true); + expect(img.attributes('src')).toBe('/le-logo.svg'); + expect(img.attributes('alt')).toBe('LE logo'); + }); - expect(img.attributes('src')).toBe(DEFAULT_PROPS.src); - expect(img.attributes('alt')).toBe(DEFAULT_PROPS.altText); + it(`throws an error when no 'altText' is provided`, () => { + expect(() => + makeWrapper({ + propsData: { src: '/le-logo.svg', altText: undefined }, + }) + ).toThrow(); }); - it('Throws an error when no altText is provided', () => { - expect(() => makeWrapper({ altText: undefined })).toThrow(); + describe(`when no 'altText' is provided and it is a decorative image`, () => { + it(`does not throw an error`, () => { + expect(() => + makeWrapper({ + propsData: { src: '/le-logo.svg', altText: undefined, isDecorative: true }, + }) + ).not.toThrow(); + }); + + it(`sets 'alt' attribute to an empty string`, () => { + const wrapper = makeWrapper({ + propsData: { src: '/le-logo.svg', altText: undefined, isDecorative: true }, + }); + expect(wrapper.exists()).toBe(true); + expect(wrapper.find('img').attributes('alt')).toBe(''); + }); }); - it('Does not throw an error when no altText is provided and it is a decorative image', () => { - expect(() => makeWrapper({ altText: undefined, isDecorative: true })).not.toThrow(); + it(`throws an error when 'aspectRatio' has an invalid format`, () => { + expect(() => + makeWrapper({ + propsData: { src: '/le-logo.svg', altText: 'LE logo', aspectRatio: '16/9' }, + }) + ).toThrow(); + }); - expect(wrapper.exists()).toBe(true); - expect(img.attributes('alt')).toBe(''); + it(`doesn't throw an error when 'aspectRatio' has a valid format`, () => { + expect(() => + makeWrapper({ + propsData: { src: '/le-logo.svg', altText: 'LE logo', aspectRatio: '16:9' }, + }) + ).not.toThrow(); }); - it('Emits an `error` event when there is an error in loading the image', () => { - makeWrapper({ - src: 'invalid-src.jpg', + it(`emits an 'error' event when there is an error in loading the image`, () => { + const wrapper = makeWrapper({ + propsData: { src: '/le-logo.svg', altText: 'LE logo' }, }); // Manually trigger the onError method to simulate the image load failure diff --git a/lib/KImg/index.vue b/lib/KImg/index.vue index e2dc212ce..20b07acf9 100644 --- a/lib/KImg/index.vue +++ b/lib/KImg/index.vue @@ -1,20 +1,93 @@ + + + diff --git a/lib/KSelect/KeenUiSelect.vue b/lib/KSelect/KeenUiSelect.vue index fb1211c9b..c74534f63 100644 --- a/lib/KSelect/KeenUiSelect.vue +++ b/lib/KSelect/KeenUiSelect.vue @@ -902,31 +902,29 @@ display: flex; align-items: flex-start; margin-bottom: $ui-input-margin-bottom; + background: $md-grey-100; + border-bottom-color: $ui-input-border-color; + border-bottom-style: solid; + border-bottom-width: $ui-input-border-width; + border-radius: 2px 2px 0 0; outline: none; &:hover:not(.is-disabled) { + border-bottom-color: $ui-input-border-color--hover; + border-bottom-width: $ui-input-border-width--active; .ui-select-label-text { color: $ui-input-label-color--hover; } - .ui-select-display { - border-bottom-color: $ui-input-border-color--hover; - } - .ui-select-dropdown-button { color: $ui-input-button-color--hover; } } &.is-active:not(.is-disabled) { - .ui-select-display { - border-bottom-width: $ui-input-border-width--active; - } - } - - &.is-active { - .ui-select-display { - border-bottom-width: $ui-input-border-width--active; + border-bottom-color: $ui-input-border-color--active; + .ui-icon { + color: $ui-input-icon-color--active; } } @@ -961,7 +959,6 @@ &:not(.is-multiple) { .ui-select-display { height: $ui-input-height; - line-height: 1; } } @@ -969,20 +966,17 @@ .ui-select-display { padding-top: rem-calc(4px); padding-bottom: rem-calc(4px); - line-height: 1.4; } } &.is-invalid:not(.is-disabled) { + border-bottom-color: $ui-input-border-color--invalid; .ui-select-label-text, + .ui-select-dropdown-button, .ui-select-icon-wrapper .ui-icon { color: $ui-input-label-color--invalid; } - .ui-select-display { - border-bottom-color: $ui-input-border-color--invalid; - } - .ui-select-feedback { color: $ui-input-feedback-color--invalid; } @@ -1050,13 +1044,11 @@ padding: 0; font-size: $ui-input-text-font-size; font-weight: normal; + line-height: 1.4; color: $ui-input-text-color; cursor: pointer; user-select: none; border: 0; - border-bottom-color: $ui-input-border-color; - border-bottom-style: solid; - border-bottom-width: $ui-input-border-width; transition: border 0.1s ease; } @@ -1110,9 +1102,6 @@ cursor: auto; background: none; border: 0; - border-bottom-color: $ui-input-border-color; - border-bottom-style: solid; - border-bottom-width: $ui-input-border-width; border-radius: 0; outline: none; transition: border 0.1s ease; diff --git a/lib/KSwitch.vue b/lib/KSwitch.vue index 8129b0013..b26052fba 100644 --- a/lib/KSwitch.vue +++ b/lib/KSwitch.vue @@ -188,7 +188,7 @@ $k-switch-height: 32px !default; $k-switch-thumb-size: 20px !default; - $k-switch-thumb-color: #fafafa !default; + $k-switch-thumb-color: #f5f5f5 !default; $k-switch-track-width: 34px !default; $k-switch-track-height: 14px !default; @@ -248,7 +248,7 @@ top: (($k-switch-thumb-size - $k-switch-track-height) / 2); width: $k-switch-track-width; height: $k-switch-track-height; - background-color: rgba(0, 0, 0, 0.26); + background-color: #cccccc; border-radius: 8px; transition: background-color 0.1s linear; } @@ -288,10 +288,10 @@ .k-switch--color-primary { &.is-checked:not(.is-disabled) { .k-switch-track { - background-color: #a5d6a7; + background-color: #b4c3fb; } .k-switch-thumb { - background-color: #4caf50; + background-color: #4368f5; } } } diff --git a/lib/KThemePlugin.js b/lib/KThemePlugin.js index 7c7315ba1..9e189f875 100644 --- a/lib/KThemePlugin.js +++ b/lib/KThemePlugin.js @@ -8,6 +8,7 @@ import KCircularLoader from './loaders/KCircularLoader'; import KDateRange from './KDateRange'; import KDropdownMenu from './KDropdownMenu'; import KEmptyPlaceholder from './KEmptyPlaceholder'; +import KListWithOverflow from './KListWithOverflow'; import KExternalLink from './buttons-and-links/KExternalLink'; import KFixedGrid from './grids/KFixedGrid'; import KFixedGridItem from './grids/KFixedGridItem'; @@ -100,6 +101,7 @@ export default function KThemePlugin(Vue) { Vue.component('KDateRange', KDateRange); Vue.component('KDropdownMenu', KDropdownMenu); Vue.component('KEmptyPlaceholder', KEmptyPlaceholder); + Vue.component('KListWithOverflow', KListWithOverflow); Vue.component('KExternalLink', KExternalLink); Vue.component('KFixedGrid', KFixedGrid); Vue.component('KFixedGridItem', KFixedGridItem); diff --git a/lib/KTooltip/index.vue b/lib/KTooltip/index.vue index 71368c70f..96923a3f0 100644 --- a/lib/KTooltip/index.vue +++ b/lib/KTooltip/index.vue @@ -13,8 +13,12 @@ class="k-tooltip" :style="{ backgroundColor: $themeTokens.text, color: $themeTokens.textInverted }" > - - + + + +
  • @@ -64,6 +68,13 @@ type: String, default: 'bottom', }, + /** + * Text of the tooltip + */ + text: { + type: String, + default: null, + }, }, data() { return { diff --git a/lib/buttons-and-links/KButton.vue b/lib/buttons-and-links/KButton.vue index 808d3b2f8..d254944cf 100644 --- a/lib/buttons-and-links/KButton.vue +++ b/lib/buttons-and-links/KButton.vue @@ -122,12 +122,12 @@ return this.hovering ? this.$themeTokens.primaryDark : this.$themeTokens.primary; } - if (this.primary) { + if (this.secondary) { return this.appearance === 'raised-button' - ? this.$themeTokens.textInverted - : this.$themeTokens.primary; + ? this.$themeBrand.textInverted + : this.$themeBrand.secondary; } else { - return this.$themeTokens.text; + return this.$themeBrand.text; } }, htmlTag() { @@ -139,9 +139,9 @@ return 'button'; }, arrowStyles() { - if (this.primary) { + if (this.secondary) { return { - fill: this.$themeTokens.textInverted, + fill: this.$themeBrand.textInverted, }; } return {}; diff --git a/lib/buttons-and-links/KRouterLink.vue b/lib/buttons-and-links/KRouterLink.vue index 544a097f6..8befc3dc3 100644 --- a/lib/buttons-and-links/KRouterLink.vue +++ b/lib/buttons-and-links/KRouterLink.vue @@ -39,7 +39,7 @@ v-if="iconAfter" :icon="iconAfter" style="top: 4px;" - :color="hovering ? $themeTokens.primaryDark : $themeTokens.primary" + :color="hovering ? $themeTokens.secondaryDark : $themeTokens.secondary" data-test="icon-after" /> diff --git a/lib/buttons-and-links/buttonMixin.js b/lib/buttons-and-links/buttonMixin.js index 2e26cb3a7..f31665353 100644 --- a/lib/buttons-and-links/buttonMixin.js +++ b/lib/buttons-and-links/buttonMixin.js @@ -63,7 +63,7 @@ export default { linkStyle() { return { color: this.$themeTokens.link, - ':hover': { color: this.$themeTokens.linkDark }, + ':hover': { color: this.$themeTokens.primaryDark }, ':focus': this.$coreOutline, ...(this.disabled ? disabledStyle : {}), }; @@ -92,21 +92,21 @@ export default { return { color: this.$themeTokens.primary, ':hover': { - backgroundColor: this.$themePalette.grey.v_300, + backgroundColor: this.$themePalette.grey.v_100, }, ':focus': { ...this.$coreOutline, outlineOffset: 0 }, ':disabled': disabledStyle, svg: { - fill: this.$themeTokens.primary, + fill: this.$themeTokens.secondary, }, }; }, secondaryRaisedStyle() { return { color: this.$themeTokens.text, - backgroundColor: this.$themePalette.grey.v_200, + backgroundColor: this.$themePalette.grey.v_50, ':hover': { - backgroundColor: this.$themePalette.grey.v_300, + backgroundColor: this.$themePalette.grey.v_100, }, ':focus': { ...this.$coreOutline, outlineOffset: '6px' }, ':disabled': disabledStyle, @@ -119,7 +119,7 @@ export default { return { color: this.$themeTokens.text, ':hover': { - backgroundColor: this.$themePalette.grey.v_300, + backgroundColor: this.$themePalette.grey.v_200, }, ':focus': { ...this.$coreOutline, outlineOffset: 0 }, ':disabled': disabledStyle, diff --git a/lib/composables/_useKResponsiveElement.js b/lib/composables/_useKResponsiveElement.js new file mode 100644 index 000000000..1387a3907 --- /dev/null +++ b/lib/composables/_useKResponsiveElement.js @@ -0,0 +1,48 @@ +import { throttle } from 'frame-throttle'; +import { onMounted, onBeforeUnmount, ref, getCurrentInstance } from '@vue/composition-api'; + +let _resizeObserver; + +if (typeof window !== 'undefined' && window.ResizeObserver) { + _resizeObserver = new ResizeObserver(entries => { + for (const entry of entries) { + if (entry.target._resizeListener) { + entry.target._resizeListener(); + } + } + }); +} + +export default function useKResponsiveElement() { + const elementWidth = ref(0); + const elementHeight = ref(0); + const instance = getCurrentInstance(); + + function updateEl() { + const { $el } = instance.proxy || {}; + const { clientHeight, clientWidth } = $el || {}; + elementWidth.value = clientWidth || 0; + elementHeight.value = clientHeight || 0; + } + + onMounted(() => { + updateEl(); + if (_resizeObserver) { + const throttledUpdateEl = throttle(updateEl); + instance.proxy.$el._resizeListener = throttledUpdateEl; + + _resizeObserver.observe(instance.proxy.$el); + } + }); + + onBeforeUnmount(() => { + if (_resizeObserver) { + _resizeObserver.unobserve(instance.proxy.$el); + } + }); + + return { + elementWidth, + elementHeight, + }; +} diff --git a/lib/__tests__/useKWindowDimensions.spec.js b/lib/composables/_useKWindowDimensions/__tests__/index.spec.js similarity index 96% rename from lib/__tests__/useKWindowDimensions.spec.js rename to lib/composables/_useKWindowDimensions/__tests__/index.spec.js index f32e5f72b..41c22c725 100644 --- a/lib/__tests__/useKWindowDimensions.spec.js +++ b/lib/composables/_useKWindowDimensions/__tests__/index.spec.js @@ -1,6 +1,6 @@ import { defineComponent } from '@vue/composition-api'; import { mount } from '@vue/test-utils'; -import useKWindowDimensions from '../useKWindowDimensions'; +import useKWindowDimensions from '../'; const resizeWindow = (width, height) => { window.innerWidth = width; diff --git a/lib/useKWindowDimensions.js b/lib/composables/_useKWindowDimensions/index.js similarity index 88% rename from lib/useKWindowDimensions.js rename to lib/composables/_useKWindowDimensions/index.js index d0e54bbdf..a799053b9 100644 --- a/lib/useKWindowDimensions.js +++ b/lib/composables/_useKWindowDimensions/index.js @@ -1,6 +1,7 @@ -import './composition-api'; //Due to @vue/composition-api shortcomings, add plugin prior to use in kolibri, studio and tests +import '../composition-api'; //Due to @vue/composition-api shortcomings, add plugin prior to use in kolibri, studio and tests import { onMounted, onUnmounted, ref } from '@vue/composition-api'; import { throttle } from 'frame-throttle'; +import { isNuxtServerSideRendering } from '../../utils'; /** Global variables */ export const windowWidth = ref(null); @@ -30,14 +31,6 @@ function windowMetrics() { }; } -/** - * Check if Nuxt is server side rendering - * @returns {Boolean} - */ -function isNuxtServerSideRendering() { - return process && process.server; -} - /** * @param {CallableFunction} eventHandler - The event callback function */ diff --git a/lib/composition-api.js b/lib/composables/composition-api.js similarity index 100% rename from lib/composition-api.js rename to lib/composables/composition-api.js diff --git a/lib/useKResponsiveWindow/MediaQuery.js b/lib/composables/useKResponsiveWindow/MediaQuery.js similarity index 83% rename from lib/useKResponsiveWindow/MediaQuery.js rename to lib/composables/useKResponsiveWindow/MediaQuery.js index 627bab61b..d241c02c4 100644 --- a/lib/useKResponsiveWindow/MediaQuery.js +++ b/lib/composables/useKResponsiveWindow/MediaQuery.js @@ -1,3 +1,5 @@ +import { isNuxtServerSideRendering } from '../../utils'; + /** * Class representing a MediaQuery */ @@ -23,21 +25,13 @@ export default class MediaQuery { return this._mediaQueryList; } - /** - * Check if Nuxt is server side rendering - * @returns {Boolean} - */ - isNuxtServerSideRendering() { - return process && process.server; - } - /** * Start listening for media query events * @returns {Object} Containing mediaQueryList, eventHandler, and stopListening */ startListening() { // Prevent function execution if Nuxt is server side rendering - if (this.isNuxtServerSideRendering() || !window.matchMedia) { + if (isNuxtServerSideRendering() || !window.matchMedia) { return; } @@ -53,7 +47,7 @@ export default class MediaQuery { */ stopListening() { // Prevent function execution if Nuxt is server side rendering - if (this.isNuxtServerSideRendering() || !window.matchMedia) { + if (isNuxtServerSideRendering() || !window.matchMedia) { return; } if (this.mediaQueryList.removeEventListener) { diff --git a/lib/useKResponsiveWindow/__tests__/useKResponsiveWindow.spec.js b/lib/composables/useKResponsiveWindow/__tests__/index.spec.js similarity index 99% rename from lib/useKResponsiveWindow/__tests__/useKResponsiveWindow.spec.js rename to lib/composables/useKResponsiveWindow/__tests__/index.spec.js index ce9d35808..4f66a7602 100644 --- a/lib/useKResponsiveWindow/__tests__/useKResponsiveWindow.spec.js +++ b/lib/composables/useKResponsiveWindow/__tests__/index.spec.js @@ -2,7 +2,7 @@ import 'mock-match-media/jest-setup.cjs'; import { setMedia } from 'mock-match-media'; import { defineComponent } from '@vue/composition-api'; import { mount } from '@vue/test-utils'; -import useKResponsiveWindow from '..'; +import useKResponsiveWindow from '../'; const resizeWindow = (width, height = 768) => { window.innerWidth = width; diff --git a/lib/useKResponsiveWindow/index.js b/lib/composables/useKResponsiveWindow/index.js similarity index 96% rename from lib/useKResponsiveWindow/index.js rename to lib/composables/useKResponsiveWindow/index.js index f9e1515be..60d25a84f 100644 --- a/lib/useKResponsiveWindow/index.js +++ b/lib/composables/useKResponsiveWindow/index.js @@ -1,5 +1,6 @@ import { computed, onBeforeUnmount, onMounted, ref, watch } from '@vue/composition-api'; -import useKWindowDimensions, { windowWidth, windowHeight } from '../useKWindowDimensions'; +import useKWindowDimensions, { windowWidth, windowHeight } from '../_useKWindowDimensions'; +import { isNuxtServerSideRendering } from '../../utils'; import MediaQuery from './MediaQuery'; /** Global variables */ @@ -32,6 +33,9 @@ const heightQuery = new MediaQuery('screen and (max-height: 600px)', event => { * Initialize media query window properties */ function initProps() { + if (isNuxtServerSideRendering()) { + return; + } if (window.matchMedia) { orientationQuery.eventHandler(orientationQuery.mediaQueryList); heightQuery.eventHandler(heightQuery.mediaQueryList); diff --git a/lib/composables/useKShow/__tests__/index.spec.js b/lib/composables/useKShow/__tests__/index.spec.js index f9b145a05..ab61106e1 100644 --- a/lib/composables/useKShow/__tests__/index.spec.js +++ b/lib/composables/useKShow/__tests__/index.spec.js @@ -1,4 +1,4 @@ -import useKShow from '../index'; +import useKShow from '../'; const { show } = useKShow(); diff --git a/lib/keen/UiTextbox.vue b/lib/keen/UiTextbox.vue index 30360d103..a22c06629 100644 --- a/lib/keen/UiTextbox.vue +++ b/lib/keen/UiTextbox.vue @@ -84,7 +84,6 @@ v-if="label || $slots.default" class="ui-textbox-label-text" :class="labelClasses" - :style="isActive ? { color: $themeTokens.primaryDark } : {}" > {{ label }}
    @@ -403,17 +402,20 @@ color: $ui-input-label-color--hover; } - .ui-textbox-input, + .ui-textbox-label, .ui-textbox-textarea { border-bottom-color: $ui-input-border-color--hover; + border-bottom-width: $ui-input-border-width--active; } } &.is-active:not(.is-disabled) { - .ui-textbox-input, + .ui-textbox-label-text { + color: $ui-input-border-color--active; + } + .ui-textbox-label, .ui-textbox-textarea { border-bottom-color: $ui-input-border-color--active; - border-bottom-width: $ui-input-border-width--active; } } @@ -465,7 +467,7 @@ color: $ui-input-label-color--invalid; } - .ui-textbox-input, + .ui-textbox-label, .ui-textbox-textarea { border-bottom-color: $ui-input-border-color--invalid; } @@ -477,6 +479,7 @@ &.is-disabled { .ui-textbox-input, + .ui-textbox-label, .ui-textbox-textarea { color: $ui-input-text-color--disabled; border-bottom-style: $ui-input-border-style--disabled; @@ -499,8 +502,11 @@ width: 100%; padding: 4px 0 0 0; margin: 0; - background: #e9e9e9; + background: $md-grey-100; border-radius: 4px 4px 0 0; + border-bottom-color: $ui-input-border-color; + border-bottom-style: solid; + border-bottom-width: $ui-input-border-width; } .ui-textbox-icon-wrapper { @@ -539,14 +545,17 @@ cursor: auto; background: none; border: none; - border-bottom-color: $ui-input-border-color; - border-bottom-style: solid; - border-bottom-width: $ui-input-border-width; border-radius: 0; outline: none; transition: border 0.1s ease; } + .ui-textbox-textarea { + border-bottom-color: $ui-input-border-color; + border-bottom-style: solid; + border-bottom-width: $ui-input-border-width; + } + .ui-textbox-input { height: $ui-input-height; } diff --git a/lib/keen/styles/md-colors.scss b/lib/keen/styles/md-colors.scss index 2c7301cdf..6c17dc03b 100644 --- a/lib/keen/styles/md-colors.scss +++ b/lib/keen/styles/md-colors.scss @@ -78,7 +78,7 @@ $md-indigo-a200: #536dfe; $md-indigo-a400: #3d5afe; $md-indigo-a700: #304ffe; -$md-blue: #2196f3; +$md-blue: #2547F3; $md-blue-50: #e3f2fd; $md-blue-100: #bbdefb; $md-blue-200: #90caf9; diff --git a/lib/keen/styles/variables.scss b/lib/keen/styles/variables.scss index 88834cf72..1bb32e97f 100644 --- a/lib/keen/styles/variables.scss +++ b/lib/keen/styles/variables.scss @@ -14,7 +14,7 @@ $brand-accent-color: $md-purple-a400 !default; $primary-text-color: rgba(black, 0.87) !default; // Secondary text color -$secondary-text-color: rgba(black, 0.54) !default; +$secondary-text-color: #666666!default; // Hint text color $hint-text-color: rgba(black, 0.38) !default; @@ -49,16 +49,17 @@ $ui-input-text-color--disabled: $disabled-text-color !default; $ui-input-text-color--invalid: $md-red !default; // Input border -$ui-input-border-color: $divider-color !default; -$ui-input-border-color--hover: rgba(black, 0.3) !default; +$ui-input-border-color: $secondary-text-color !default; +$ui-input-border-color--hover: rgba(black, 0.75) !default; $ui-input-border-color--active: $brand-primary-color !default; $ui-input-border-color--invalid: $md-red !default; $ui-input-border-width: 1px !default; -$ui-input-border-width--active: 2px !default; +$ui-input-border-width--active: 1.5px !default; $ui-input-border-style--disabled: dotted !default; // Input icons $ui-input-icon-color: $secondary-text-color !default; +$ui-input-icon-color--active: $brand-primary-color !default; $ui-input-icon-opacity--disabled: 0.6 !default; $ui-input-icon-margin-right: rem(12px) !default; $ui-input-icon-margin-top: rem(4px) !default; @@ -68,7 +69,7 @@ $ui-input-icon-margin-top--with-label: rem(24px) !default; $ui-input-button-color: $secondary-text-color !default; $ui-input-button-color--hover: $primary-text-color !default; $ui-input-button-opacity--disabled: 0.6 !default; -$ui-input-button-size: rem(18px) !default; +$ui-input-button-size: rem(24px) !default; $ui-input-button-margin-top: rem(7px) !default; $ui-input-button-margin-top--with-label: rem(27px) !default; diff --git a/lib/loaders/KLinearLoader.vue b/lib/loaders/KLinearLoader.vue index 4027e4780..9f2ca6e7a 100644 --- a/lib/loaders/KLinearLoader.vue +++ b/lib/loaders/KLinearLoader.vue @@ -11,7 +11,7 @@