From bf96b974c158da3e4ad8de49b6b6df38c92a511c Mon Sep 17 00:00:00 2001 From: Nikita Polyakov <53777036+Nikita-Polyakov@users.noreply.github.com> Date: Mon, 31 May 2021 17:15:16 +0300 Subject: [PATCH 01/73] Release/0.9.0 (#207) * Neomorphism. Button. (#205) * add color variables * add provide inject components * fix inject design system * add primary secondary tertiary styles * add action and alternative states * remove duplicated styles * fefactoring neo css vars usage * fix ln * fix export * fix config * remove console.log * rename neo to neu * Neumorphism/input (#206) * wip neumorphic input * add neu input styles & refactoring * fix file input * export setDesignTheme * Update STooltip.stories.ts (#197) Co-authored-by: Alexander Yakimov Co-authored-by: Stefan Popov --- build/rollup.config.js | 5 +- package.json | 4 +- src/components/Button/SButton.vue | 6 +- .../DesignSystem/DesignSystemInject.ts | 13 + .../DesignSystem/SDesignSystemProvider.vue | 24 ++ src/components/DesignSystem/consts.ts | 6 + src/components/DesignSystem/index.ts | 9 + src/components/Input/SInput.vue | 91 ++++--- src/components/index.ts | 2 + src/index.ts | 10 +- src/store/Theme.ts | 16 +- src/stories/SButton.stories.ts | 49 ++-- src/stories/SFloatInput.stories.ts | 17 +- src/stories/SInput.stories.ts | 32 ++- src/stories/STooltip.stories.ts | 17 ++ src/styles/index.scss | 1 + src/styles/input.scss | 214 +++++++++------- src/styles/neumorphism/button.scss | 237 ++++++++++++++++++ src/styles/neumorphism/index.scss | 3 + src/styles/neumorphism/input.scss | 49 ++++ src/styles/neumorphism/variables.scss | 70 ++++++ src/types/components.ts | 1 + src/utils/DesignSystem.ts | 4 + src/utils/index.ts | 4 + yarn.lock | 8 +- 25 files changed, 726 insertions(+), 166 deletions(-) create mode 100644 src/components/DesignSystem/DesignSystemInject.ts create mode 100644 src/components/DesignSystem/SDesignSystemProvider.vue create mode 100644 src/components/DesignSystem/consts.ts create mode 100644 src/components/DesignSystem/index.ts create mode 100644 src/styles/neumorphism/button.scss create mode 100644 src/styles/neumorphism/index.scss create mode 100644 src/styles/neumorphism/input.scss create mode 100644 src/styles/neumorphism/variables.scss create mode 100644 src/utils/DesignSystem.ts diff --git a/build/rollup.config.js b/build/rollup.config.js index aa95fda3..2d7a79bc 100644 --- a/build/rollup.config.js +++ b/build/rollup.config.js @@ -33,7 +33,7 @@ export default { targets: [ { src: 'src/assets/*', dest: 'lib/assets' }, { - src: 'src/styles/*', + src: 'src/styles/*.scss', dest: 'lib/styles', // Replace all imports for scss files which will be used as theming files transform: (content) => { @@ -43,7 +43,8 @@ export default { .replace('../../node_modules/element-ui/packages/theme-chalk/src/index', './element-ui/index') } }, - { src: 'node_modules/element-ui/packages/theme-chalk/src/*', dest: 'lib/styles/element-ui' } + { src: 'node_modules/element-ui/packages/theme-chalk/src/*', dest: 'lib/styles/element-ui' }, + { src: 'src/styles/neumorphism/*', dest: 'lib/styles/neumorphism' } ] }), typescript({ diff --git a/package.json b/package.json index 6e8bcc79..23486122 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@soramitsu/soramitsu-js-ui", - "version": "0.8.9", + "version": "0.9.0", "private": false, "publishConfig": { "registry": "https://nexus.iroha.tech/repository/npm-soramitsu/" @@ -71,7 +71,7 @@ "postcss": "^8.2.10", "rollup": "^1.27.8", "rollup-plugin-commonjs": "^10.1.0", - "rollup-plugin-copy": "^3.3.0", + "rollup-plugin-copy": "^3.4.0", "rollup-plugin-delete": "^1.1.0", "rollup-plugin-scss": "^2.5.0", "rollup-plugin-terser": "^5.1.2", diff --git a/src/components/Button/SButton.vue b/src/components/Button/SButton.vue index 7d971e2a..b4d15841 100644 --- a/src/components/Button/SButton.vue +++ b/src/components/Button/SButton.vue @@ -26,6 +26,7 @@ import { ElForm } from 'element-ui/types/form' import { ElFormItem } from 'element-ui/types/form-item' import { PopoverPlacement } from 'element-ui/types/popover' +import { DesignSystemInject } from '../DesignSystem' import SizeMixin from '../../mixins/SizeMixin' import BorderRadiusMixin from '../../mixins/BorderRadiusMixin' import { SIcon } from '../Icon' @@ -35,7 +36,7 @@ import { ButtonTypes, ButtonNativeTypes, ButtonIconPosition } from './consts' @Component({ components: { SIcon, STooltip } }) -export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin) { +export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin, DesignSystemInject) { readonly IconPosition = ButtonIconPosition readonly ButtonTypes = ButtonTypes /** @@ -119,6 +120,9 @@ export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin) { get computedClasses (): Array { const cssClasses: Array = [] + if (this.designSystemClass) { + cssClasses.push(this.designSystemClass) + } if ((this.elForm || this.elFormItem || {}).size) { cssClasses.push(`s-${(this.elForm || this.elFormItem).size}`) } else if (this.isStandardSize) { diff --git a/src/components/DesignSystem/DesignSystemInject.ts b/src/components/DesignSystem/DesignSystemInject.ts new file mode 100644 index 00000000..9b276cee --- /dev/null +++ b/src/components/DesignSystem/DesignSystemInject.ts @@ -0,0 +1,13 @@ +import { Component, Vue, Inject, Prop } from 'vue-property-decorator' +import { DesignSystemProvideKey } from './consts' +import { DesignSystemTypes } from '../../utils/DesignSystem' + +@Component +export default class DesignSystemInject extends Vue { + @Prop({ default: true, type: Boolean }) readonly useDesignSystem!: boolean + @Inject({ from: DesignSystemProvideKey, default: DesignSystemTypes.DEFAULT }) readonly designSystem!: any + + get designSystemClass (): string { + return this.useDesignSystem ? this.designSystem.value : '' + } +} diff --git a/src/components/DesignSystem/SDesignSystemProvider.vue b/src/components/DesignSystem/SDesignSystemProvider.vue new file mode 100644 index 00000000..a6922930 --- /dev/null +++ b/src/components/DesignSystem/SDesignSystemProvider.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/src/components/DesignSystem/consts.ts b/src/components/DesignSystem/consts.ts new file mode 100644 index 00000000..8b8a958c --- /dev/null +++ b/src/components/DesignSystem/consts.ts @@ -0,0 +1,6 @@ +export const DesignSystemProvideKey = 'designSystem' + +export enum DesignSystemTypes { + DEFAULT = '', + NEUMORPHIC = 'neumorphic' +} diff --git a/src/components/DesignSystem/index.ts b/src/components/DesignSystem/index.ts new file mode 100644 index 00000000..32bb8ea2 --- /dev/null +++ b/src/components/DesignSystem/index.ts @@ -0,0 +1,9 @@ +import SDesignSystemProvider from './SDesignSystemProvider.vue' +import DesignSystemInject from './DesignSystemInject' +import { DesignSystemProvideKey } from './consts' + +export { + SDesignSystemProvider, + DesignSystemProvideKey, + DesignSystemInject +} diff --git a/src/components/Input/SInput.vue b/src/components/Input/SInput.vue index e80d1ffd..9efb076e 100644 --- a/src/components/Input/SInput.vue +++ b/src/components/Input/SInput.vue @@ -3,40 +3,51 @@ class="s-input" :class="computedClasses" > - {{ placeholder }} - - - - + + +
+ + +
+ {{ placeholder }} + + + + +
+ +
+ + @@ -46,13 +57,14 @@ import { ElInput } from 'element-ui/types/input' import { ElForm } from 'element-ui/types/form' import { SIcon } from '../Icon' +import { DesignSystemInject } from '../DesignSystem' import BorderRadiusMixin from '../../mixins/BorderRadiusMixin' import { Autocomplete, InputSize, InputType } from './consts' @Component({ components: { SIcon } }) -export default class SInput extends Mixins(BorderRadiusMixin) { +export default class SInput extends Mixins(BorderRadiusMixin, DesignSystemInject) { readonly InputType = InputType readonly emptyValue = null /** @@ -170,6 +182,9 @@ export default class SInput extends Mixins(BorderRadiusMixin) { get computedClasses (): Array { const cssClasses: Array = [] + if (this.designSystemClass) { + cssClasses.push(this.designSystemClass) + } if (this.focused) { cssClasses.push('s-focused') } @@ -191,6 +206,12 @@ export default class SInput extends Mixins(BorderRadiusMixin) { if (this.size) { cssClasses.push(this.sizeClass) } + if (this.prefix) { + cssClasses.push('s-input--prefix') + } + if (this.suffix) { + cssClasses.push('s-input--suffix') + } return cssClasses } diff --git a/src/components/index.ts b/src/components/index.ts index ec7cbf6d..b2f4448c 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -12,6 +12,7 @@ import { SCol } from './Layout/Col' import { SCollapse, SCollapseItem } from './Collapse' import { SContainer } from './Layout/Container' import { SDatePicker } from './DatePicker' +import { SDesignSystemProvider } from './DesignSystem' import { SDialog } from './Dialog' import { SDivider } from './Divider' import { SDropdown, SDropdownItem } from './Dropdown' @@ -47,6 +48,7 @@ export { SCollapseItem, SContainer, SDatePicker, + SDesignSystemProvider, SDialog, SDivider, SDropdown, diff --git a/src/index.ts b/src/index.ts index 8f4cf815..9f43d725 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import { SCollapseItem, SContainer, SDatePicker, + SDesignSystemProvider, SDialog, SDivider, SDropdown, @@ -53,7 +54,9 @@ import { Float, Integer } from './directives' import { Components } from './types/components' import { Directives } from './types/directives' import { modules, Modules } from './store' -import { setTheme } from './utils' +import { setTheme, setDesignSystem } from './utils' +import { DesignSystemTypes } from './utils/DesignSystem' +import { Themes } from './utils/Theme' import { Loading, Message, MessageBox, Notification } from './plugins/elementUI' import { SDialogMixin } from './mixins' @@ -71,6 +74,7 @@ const components = [ { component: SCollapseItem, name: Components.SCollapseItem }, { component: SContainer, name: Components.SContainer }, { component: SDatePicker, name: Components.SDatePicker }, + { component: SDesignSystemProvider, name: Components.SDesignSystemProvider }, { component: SDialog, name: Components.SDialog }, { component: SDivider, name: Components.SDivider }, { component: SDropdown, name: Components.SDropdown }, @@ -131,6 +135,9 @@ if (typeof window !== 'undefined' && window.Vue) { export { setTheme, + Themes, + setDesignSystem, + DesignSystemTypes, Loading, Message, MessageBox, @@ -148,6 +155,7 @@ export { SCollapseItem, SContainer, SDatePicker, + SDesignSystemProvider, SDialog, SDivider, SDropdown, diff --git a/src/store/Theme.ts b/src/store/Theme.ts index 0e0ccf66..b30acf28 100644 --- a/src/store/Theme.ts +++ b/src/store/Theme.ts @@ -5,11 +5,13 @@ import flow from 'lodash/fp/flow' import concat from 'lodash/fp/concat' import { Theme, Themes } from '../utils/Theme' +import { DesignSystemTypes } from '../utils/DesignSystem' const types = flow( flatMap(x => [x + '_REQUEST', x + '_SUCCESS', x + '_FAILURE']), concat([ - 'CHANGE_THEME' + 'CHANGE_THEME', + 'SET_DESIGN_SYSTEM' ]), map(x => [x, x]), fromPairs @@ -17,7 +19,8 @@ const types = flow( function initialState () { return { - theme: Themes.LIGHT + theme: Themes.LIGHT, + designSystem: DesignSystemTypes.DEFAULT } } @@ -26,18 +29,27 @@ const state = initialState() const getters = { libraryTheme (state) { return state.theme + }, + libraryDesignSystem (state) { + return state.designSystem } } const mutations = { [types.CHANGE_THEME] (state, theme: Theme) { state.theme = theme + }, + [types.SET_DESIGN_SYSTEM] (state, designSystem: DesignSystemTypes) { + state.designSystem = designSystem } } const actions = { changeTheme ({ commit }, { theme }) { commit(types.CHANGE_THEME, theme) + }, + setDesignSystem ({ commit }, designSystem: DesignSystemTypes) { + commit(types.SET_DESIGN_SYSTEM, designSystem) } } diff --git a/src/stories/SButton.stories.ts b/src/stories/SButton.stories.ts index 6b90d8c3..e38ebff7 100644 --- a/src/stories/SButton.stories.ts +++ b/src/stories/SButton.stories.ts @@ -1,8 +1,9 @@ import { text, boolean, select, withKnobs } from '@storybook/addon-knobs' -import { SButton, SButtonGroup, SRow, SCol, SMain } from '../components' +import { SButton, SButtonGroup, SRow, SCol, SMain, SDesignSystemProvider } from '../components' import { Size, BorderRadius } from '../types' import { ButtonTypes, ButtonIconPosition } from '../components/Button' +import { DesignSystemTypes } from '../utils/DesignSystem' export default { component: SButton, @@ -12,22 +13,28 @@ export default { } export const configurable = () => ({ - components: { SButton }, - template: ` + + > {{ type !== 'action' ? 'Default' : '' }} - `, + + `, props: { + designSystem: { + default: select('Design System', Object.values(DesignSystemTypes), DesignSystemTypes.DEFAULT) + }, disabled: { default: boolean('Disabled', false) }, @@ -70,19 +77,25 @@ export const differentTypeButtonsData = Object.values(ButtonTypes).map(type => { return data }) export const withDifferentTypes = () => ({ - components: { SButton, SRow }, - template: ` - - {{ item.label }} - - `, + components: { SButton, SRow, SDesignSystemProvider }, + template: ` + + + + {{ item.label }} + + + `, props: { + designSystem: { + default: select('Design System', Object.values(DesignSystemTypes), DesignSystemTypes.DEFAULT) + }, items: { default: () => differentTypeButtonsData } diff --git a/src/stories/SFloatInput.stories.ts b/src/stories/SFloatInput.stories.ts index 7c0234d8..13309571 100644 --- a/src/stories/SFloatInput.stories.ts +++ b/src/stories/SFloatInput.stories.ts @@ -1,6 +1,7 @@ -import { number, text, boolean, object, withKnobs } from '@storybook/addon-knobs' +import { number, text, boolean, object, select, withKnobs } from '@storybook/addon-knobs' -import { SFloatInput, SRow } from '../components' +import { SFloatInput, SRow, SDesignSystemProvider } from '../components' +import { DesignSystemTypes } from '../utils/DesignSystem' export default { component: SFloatInput, @@ -9,8 +10,10 @@ export default { } export const configurable = () => ({ - components: { SFloatInput, SRow }, - template: ` + components: { SFloatInput, SRow, SDesignSystemProvider }, + template: ` + + ({ :delimiters="delimiters" :max="max" /> - `, + + `, data: () => ({ model: '' }), props: { + designSystem: { + default: select('Design System', Object.values(DesignSystemTypes), DesignSystemTypes.DEFAULT) + }, decimals: { default: number('Decimals', 18) }, diff --git a/src/stories/SInput.stories.ts b/src/stories/SInput.stories.ts index 8e394b96..29981c57 100644 --- a/src/stories/SInput.stories.ts +++ b/src/stories/SInput.stories.ts @@ -1,8 +1,9 @@ import { text, boolean, withKnobs, number, select } from '@storybook/addon-knobs' -import { SInput, SRow, SCol } from '../components' +import { SInput, SRow, SCol, SDesignSystemProvider } from '../components' import { BorderRadius } from '../types' import { InputType, InputSize } from '../components/Input' +import { DesignSystemTypes } from '../utils/DesignSystem' export default { component: SInput, @@ -12,8 +13,10 @@ export default { } export const configurable = () => ({ - components: { SInput }, - template: ` + ({ :size="size" :prefix="prefix" :suffix="suffix" - />`, + > +
{{ top }}
+
{{ bottom }}
+
{{ left }}
+
{{ right }}
+
+
`, data: () => ({ input: '' }), props: { + designSystem: { + default: select('Design System', Object.values(DesignSystemTypes), DesignSystemTypes.DEFAULT) + }, + top: { + default: text('Top slot content', '') + }, + bottom: { + default: text('Bottom slot content', '') + }, + left: { + default: text('Left slot content', '') + }, + right: { + default: text('Right slot content', '') + }, type: { default: select('Type', Object.values(InputType), InputType.TEXT) }, diff --git a/src/stories/STooltip.stories.ts b/src/stories/STooltip.stories.ts index fcd35aa9..f5b8bbe4 100644 --- a/src/stories/STooltip.stories.ts +++ b/src/stories/STooltip.stories.ts @@ -135,3 +135,20 @@ export const withManualMode = () => ({ ` }) + +export const withDifferentContent = () => ({ + components: { STooltip, SButton, SRow }, + template: ` + Click + `, + data: () => ({ + content: 'Click' + }), + methods: { + async handleClick () { + (this as any).content = 'Clicked!' + await new Promise((resolve) => setTimeout(resolve, 400)); + (this as any).content = 'Click' + } + } +}) diff --git a/src/styles/index.scss b/src/styles/index.scss index 93009ec5..c3775b6d 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,3 +1,4 @@ +@import "./neumorphism/index.scss"; @import "./variables"; @import "./element-variables"; @import "./common"; diff --git a/src/styles/input.scss b/src/styles/input.scss index 35cf091b..626971f0 100644 --- a/src/styles/input.scss +++ b/src/styles/input.scss @@ -4,117 +4,157 @@ ) { &.s-border-radius { &-#{$suffix} { - border-radius: 0; - .s-placeholder { - border-top-left-radius: $border-radius; - } - .el-input, - .el-textarea { - > input, - > textarea { - border-radius: $border-radius; - } - } + border-radius: $border-radius; } } } +$input-icon-width: 18px; +$input-border-width: 1px; +$input-padding-top: $s-padding-input / 2 - $input-border-width; +$input-padding-left: $s-padding-input - $input-border-width; + .s-input { + display: flex; + flex-direction: column; + justify-content: center; + + background-color: var(--s-color-base-background); + border-color: var(--s-color-base-background); + border-style: solid; + border-width: $input-border-width; font-family: $s-font-family-default; - width: 100%; min-height: $s-size-big; + padding: $input-padding-top $input-padding-left; position: relative; + width: 100%; + @include input-border-radius; @include input-border-radius("medium", var(--s-border-radius-medium)); @include input-border-radius("small", var(--s-border-radius-small)); @include input-border-radius("mini", var(--s-border-radius-mini)); - .el-input__suffix { - z-index: 1; + + &__content { + display: flex; + align-items: center; + width: 100%; } + + &__input { + flex: 1; + position: relative; + } + + &.s-size-medium { + min-height: $s-size-medium; + } + .s-placeholder { // TODO: add default animation from material-ui color: var(--s-color-base-content-tertiary); text-align: left; font-size: var(--s-font-size-mini); - padding: 0 15px; - padding-top: 5px; - top: 1px; - left: 1px; - position: absolute; + line-height: 1.5; z-index: 1; - width: calc(100% - 15px); - background-color: var(--s-color-base-background); pointer-events: none; - } - .el-input, - .el-textarea { - height: 100%; - > input, - > textarea { - height: $s-size-big; - border: 1px solid var(--s-color-base-background); - background-color: var(--s-color-base-background); - &::placeholder { - color: var(--s-color-base-content-tertiary); - opacity: 1; // Firefox + position: absolute; + top: -3px; + + & + .el-input, & + .el-textarea { + padding-top: $s-padding-input; + + .el-input__validateIcon { + padding-top: 11px; } } - > textarea { - padding-top: 12px; - } - [class^="el-input__count"] { - background-color: transparent; - } } + &:hover { - .s-placeholder, - .el-input > input, - .el-textarea > textarea { - background-color: var(--s-color-base-background-hover); - } - .el-input > input, - .el-textarea > textarea { - border-color: var(--s-color-base-background-hover); - } + background-color: var(--s-color-base-background-hover); + border-color: var(--s-color-base-background-hover); } - &.s-size-medium { - min-height: $s-size-medium; - .el-input, - .el-textarea { - height: 100%; - > input, - > textarea { - height: $s-size-medium; - } - } - } &.s-focused { - .s-placeholder, - .el-input > input, - .el-textarea > textarea { - background-color: var(--s-color-utility-surface); - } - .el-input > input, - .el-textarea > textarea { - border-color: var(--s-color-base-border-primary); - } + background-color: var(--s-color-utility-surface); + border-color: var(--s-color-base-border-primary); } + &.s-disabled { - .s-placeholder, - .el-input > input, - .el-textarea > textarea { + color: var(--s-color-base-content-quaternary); + background-color: var(--s-color-base-disabled); + border-color: var(--s-color-base-disabled); + + .s-placeholder { color: var(--s-color-base-content-quaternary); - background-color: var(--s-color-base-disabled); } - .el-input > input, - .el-textarea > textarea { - border-color: var(--s-color-base-disabled); + } + + &--prefix:not(.s-textarea) { + .s-placeholder { + padding-left: $input-icon-width + $s-padding-input / 2; } } - &:not(.s-textarea) { + + &--suffix:not(.s-textarea) { .s-placeholder { - background-color: transparent !important; + padding-right: $input-icon-width + $s-padding-input / 2; + } + } + + .el-input, .el-textarea { + font-size: inherit; + position: unset; + + [class^="el-input__count"] { + background-color: transparent; + } + + &.is-disabled { + .el-input__inner { + background-color: transparent; + color: inherit; + } + } + + &__inner { + height: auto; + line-height: 1.5; + border: none; + border-radius: 0; + background-color: transparent; + padding: 0; + text-align: left; + + &::placeholder { + color: var(--s-color-base-content-tertiary); + opacity: 1; // Firefox + } + } + + &__icon { + line-height: 1; + width: $input-icon-width; + } + + &__prefix { + z-index: 1; + left: 0; + } + + &__suffix { + z-index: 1; + right: 0; + } + + &--prefix { + .el-input__inner { + padding-left: $input-icon-width + $s-padding-input / 2; + } + } + &--suffix { + .el-input__inner { + padding-right: $input-icon-width + $s-padding-input / 2; + } } } .el-input > input { @@ -132,27 +172,17 @@ animation-name: onAutoFillCancel; } } - .s-placeholder + .el-input { - > input { - padding-top: 12px; - } - .el-input__validateIcon { - padding-top: 11px; - } - } - .s-placeholder + .el-textarea > textarea { - padding-top: 24px; - } + &.s-text-file { .el-input > input { padding-right: 56px; } .s-icon-file-file-upload-24 { - top: 16px; - right: 16px; + right: 0; font-size: var(--s-icon-font-size-big); - z-index: 1; + z-index: 2; position: absolute; + cursor: pointer; + input { cursor: pointer; position: absolute; @@ -161,7 +191,7 @@ right: 0; width: 56px; height: 100%; - z-index: 2; + z-index: 1; } } } diff --git a/src/styles/neumorphism/button.scss b/src/styles/neumorphism/button.scss new file mode 100644 index 00000000..c781ddf2 --- /dev/null +++ b/src/styles/neumorphism/button.scss @@ -0,0 +1,237 @@ +// Neu button common variables +$neu-button-border-width: 2px !default; +$neu-button-border-style: solid !default; +$neu-button-transition: all 0.25s ease-in-out !default; + +$neu-button-background-color-disabled: var(--neu-color-utility-surface) !default; +$neu-button-border-color-disabled: var(--neu-color-utility-body) !default; +$neu-button-box-shadow-disabled: -1px -1px 5px rgba(255, 255, 255, 0.6) !default; +$neu-button-text-color-disabled: var(--neu-color-base-content-tertiary) !default; + +// Primary button: common +$neu-button-primary-border-width: $neu-button-border-width !default; +$neu-button-primary-border-width-alternative: 0px !default; +$neu-button-primary-border-style: $neu-button-border-style !default; +// Primary button: background colors +$neu-button-primary-background-color: var(--neu-color-theme-accent) !default; +$neu-button-primary-background-color-hover: var(--neu-color-theme-accent-hover) !default; +$neu-button-primary-background-color-pressed: var(--neu-color-theme-accent-pressed) !default; +$neu-button-primary-background-color-alternative: var(--neu-color-theme-secondary) !default; +$neu-button-primary-background-color-alternative-active: var(--neu-color-theme-secondary-hover) !default; +// Primary button: text colors +$neu-button-primary-text-color: var(--neu-color-base-on-accent) !default; +$neu-button-primary-text-color-hover: $neu-button-primary-text-color !default; +$neu-button-primary-text-color-pressed: $neu-button-primary-text-color !default; +// Primary button: border colors +$neu-button-primary-border-color: var(--neu-color-base-border-secondary) !default; +$neu-button-primary-border-color-hover: var(--neu-color-utility-surface) !default; +$neu-button-primary-border-color-pressed: $neu-button-primary-border-color-hover !default; +// Primary button: box-shadow +$neu-button-primary-box-shadow: 1px 1px 5px var(--neu-color-base-on-accent), -1px -1px 5px var(--neu-color-base-on-accent) !default; +$neu-button-primary-box-shadow-hover: 1px 1px 5px rgba(255, 255, 255, 0.7), -1px -1px 5px var(--neu-color-base-on-accent), 0px 0px 20px rgba(247, 84, 163, 0.5) !default; +$neu-button-primary-box-shadow-pressed: $neu-button-primary-box-shadow-hover !default; +$neu-button-primary-box-shadow-alternative: none !default; + +// Secondary button: common +$neu-button-secondary-border-width: 0px !default; +$neu-button-secondary-border-style: $neu-button-border-style !default; +// Secondary button: background colors +$neu-button-secondary-background-color: var(--neu-color-base-content-tertiary) !default; +$neu-button-secondary-background-color-active: var(--neu-color-base-content-secondary) !default; +$neu-button-secondary-background-color-alternative: var(--neu-color-utility-body) !default; +$neu-button-secondary-background-color-alternative-active: var(--neu-color-base-content-tertiary) !default; +// Secondary button: text colors +$neu-button-secondary-text-color: var(--neu-color-base-on-accent) !default; +$neu-button-secondary-text-color-active: $neu-button-secondary-text-color !default; +$neu-button-secondary-text-color-alternative: var(--neu-color-base-content-tertiary) !default; +$neu-button-secondary-text-color-alternative-active: var(--neu-color-base-on-accent) !default; +// Secondary button: border colors +$neu-button-secondary-border-color: transparent !default; +$neu-button-secondary-border-color-active: $neu-button-secondary-border-color !default; +// Secondary button: box-shadow +$neu-button-secondary-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1), inset 1px 1px 1px rgba(0, 0, 0, 0.1) !default; +$neu-button-secondary-box-shadow-active: $neu-button-secondary-box-shadow !default; +$neu-button-secondary-box-shadow-alternative: -1px -1px 1px rgba(0, 0, 0, 0.02), 1px 1px 3px rgba(0, 0, 0, 0.1), inset 1px 1px 2px rgba(255, 255, 255, 0.8) !default; + +// Tertiary button: common +$neu-button-tertiary-border-width: 0px !default; +$neu-button-tertiary-border-style: $neu-button-border-style !default; +// Tertiary button: background colors +$neu-button-tertiary-background-color: var(--neu-color-utility-surface) !default; +$neu-button-tertiary-background-color-active: $neu-button-tertiary-background-color !default; +$neu-button-tertiary-background-color-alternative: var(--neu-color-utility-body) !default; +// Tertiary button: text colors +$neu-button-tertiary-text-color: var(--neu-color-base-content-secondary) !default; +$neu-button-tertiary-text-color-active: var(--neu-color-base-content-primary) !default; +// Tertiary button: border colors +$neu-button-tertiary-border-color: transparent !default; +$neu-button-tertiary-border-color-active: $neu-button-tertiary-border-color !default; +// Tertiary button: box-shadow +$neu-button-tertiary-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1), inset 1px 1px 2px #FFFFFF !default; +$neu-button-tertiary-box-shadow-active: $neu-button-tertiary-box-shadow !default; + +// Action button: common +$neu-button-action-border-width: 0px !default; +$neu-button-action-border-style: $neu-button-border-style !default; +// Action button: background colors +$neu-button-action-background-color: var(--neu-color-utility-body) !default; +$neu-button-action-background-color-hover: $neu-button-action-background-color !default; +$neu-button-action-background-color-pressed: $neu-button-action-background-color !default; +$neu-button-action-background-color-alternative: transparent !default; +// Action button: text colors +$neu-button-action-text-color: var(--neu-color-base-content-tertiary) !default; +$neu-button-action-text-color-hover: var(--neu-color-base-content-secondary) !default; +$neu-button-action-text-color-pressed: var(--neu-color-theme-accent) !default; +$neu-button-action-text-color-alternative: var(--neu-color-base-content-tertiary) !default; +$neu-button-action-text-color-alternative-active: var(--neu-color-base-content-secondary) !default; +// Action button: border colors +$neu-button-action-border-color: transparent !default; +$neu-button-action-border-color-hover: $neu-button-action-border-color !default; +$neu-button-action-border-color-pressed: $neu-button-action-border-color !default; +$neu-button-action-border-color-alternative: transparent !default; +// Action button: box-shadow +$neu-button-action-box-shadow: -1px -1px 1px rgba(0, 0, 0, 0.02), 1px 1px 3px rgba(0, 0, 0, 0.1), inset 1px 1px 2px rgba(255, 255, 255, 0.8) !default; +$neu-button-action-box-shadow-hover: $neu-button-action-box-shadow !default; +$neu-button-action-box-shadow-pressed: 1px 1px 2px #FFFFFF, inset 1px 1px 2px rgba(0, 0, 0, 0.1) !default; +$neu-button-action-box-shadow-alternative: none !default; + +@mixin disabled ( + $border-width: $neu-button-border-width, + $background-color: $neu-button-background-color-disabled, + $box-shadow: $neu-button-box-shadow-disabled +) { + &:disabled, &:disabled:hover { + color: $neu-button-text-color-disabled; + background-color: $background-color; + border-color: $neu-button-border-color-disabled; + box-shadow: $box-shadow; + border-width: $border-width; + } +} + +.el-button.neumorphic { + transition: $neu-button-transition; + + &.s-primary { + background: $neu-button-primary-background-color; + border-color: $neu-button-primary-border-color; + border-style: $neu-button-primary-border-style; + border-width: $neu-button-primary-border-width; + box-shadow: $neu-button-primary-box-shadow; + color: $neu-button-primary-text-color; + + &:hover, &:focus, &.focusing { + background: $neu-button-primary-background-color-hover; + border-color: $neu-button-primary-border-color-hover; + color: $neu-button-primary-text-color-hover; + box-shadow: $neu-button-primary-box-shadow-hover; + } + &:active, &.s-pressed { + background: $neu-button-primary-background-color-pressed; + border-color: $neu-button-primary-border-color-pressed; + color: $neu-button-primary-text-color-pressed; + box-shadow: $neu-button-primary-box-shadow-pressed; + } + @include disabled; + + &.s-alternative { + background: $neu-button-primary-background-color-alternative; + border-width: $neu-button-primary-border-width-alternative; + box-shadow: $neu-button-primary-box-shadow-alternative; + + &:hover, &:focus, &.focusing, &:active, &.s-pressed { + background: $neu-button-primary-background-color-alternative-active; + } + + @include disabled($neu-button-primary-border-width-alternative); + } + } + + &.s-secondary { + background: $neu-button-secondary-background-color; + border-color: $neu-button-secondary-border-color; + border-style: $neu-button-secondary-border-style; + border-width: $neu-button-secondary-border-width; + box-shadow: $neu-button-secondary-box-shadow; + color: $neu-button-secondary-text-color; + + &:hover, &:focus, &.focusing, &:active, &.s-pressed { + background: $neu-button-secondary-background-color-active; + border-color: $neu-button-secondary-border-color-active; + color: $neu-button-secondary-text-color-active; + box-shadow: $neu-button-secondary-box-shadow-active; + } + @include disabled($neu-button-secondary-border-width); + + &.s-alternative { + background: $neu-button-secondary-background-color-alternative; + box-shadow: $neu-button-secondary-box-shadow-alternative; + color: $neu-button-secondary-text-color-alternative; + + &:hover, &:focus, &.focusing, &:active, &.s-pressed { + background: $neu-button-secondary-background-color-alternative-active; + color: $neu-button-secondary-text-color-alternative-active; + } + @include disabled($neu-button-secondary-border-width); + } + } + + &.s-tertiary { + background: $neu-button-tertiary-background-color; + border-color: $neu-button-tertiary-border-color; + border-style: $neu-button-tertiary-border-style; + border-width: $neu-button-tertiary-border-width; + box-shadow: $neu-button-tertiary-box-shadow; + color: $neu-button-tertiary-text-color; + + &:hover, &:focus, &.focusing, &:active, &.s-pressed { + background: $neu-button-tertiary-background-color-active; + border-color: $neu-button-tertiary-border-color-active; + color: $neu-button-tertiary-text-color-active; + box-shadow: $neu-button-tertiary-box-shadow-active; + } + @include disabled($neu-button-tertiary-border-width); + + &.s-alternative { + background: $neu-button-tertiary-background-color-alternative; + + @include disabled($neu-button-tertiary-border-width); + } + } + + &.s-action { + background: $neu-button-action-background-color; + border-color: $neu-button-action-border-color; + border-style: $neu-button-action-border-style; + border-width: $neu-button-action-border-width; + box-shadow: $neu-button-action-box-shadow; + color: $neu-button-action-text-color; + + &:hover, &:focus, &.focusing { + background: $neu-button-action-background-color-hover; + border-color: $neu-button-action-border-color-hover; + color: $neu-button-action-text-color-hover; + box-shadow: $neu-button-action-box-shadow-hover; + } + &:active, &.s-pressed { + background: $neu-button-action-background-color-pressed; + border-color: $neu-button-action-border-color-pressed; + color: $neu-button-action-text-color-pressed; + box-shadow: $neu-button-action-box-shadow-pressed; + } + @include disabled($neu-button-action-border-width, $neu-button-action-background-color, $neu-button-action-box-shadow-pressed); + &.s-alternative { + color: $neu-button-action-text-color-alternative; + + &, &:hover, &:focus, &.focusing, &:active, &.s-pressed { + background-color: $neu-button-action-background-color-alternative; + border-color: $neu-button-action-border-color-alternative; + box-shadow: $neu-button-action-box-shadow-alternative; + } + &:hover, &:focus, &.focusing, &:active, &.s-pressed { + color: $neu-button-action-text-color-alternative-active; + } + @include disabled($neu-button-action-border-width, $neu-button-action-background-color-alternative, $neu-button-action-box-shadow-alternative); + } + } +} diff --git a/src/styles/neumorphism/index.scss b/src/styles/neumorphism/index.scss new file mode 100644 index 00000000..6f709c44 --- /dev/null +++ b/src/styles/neumorphism/index.scss @@ -0,0 +1,3 @@ +@import "./variables"; +@import "./button.scss"; +@import "./input.scss"; diff --git a/src/styles/neumorphism/input.scss b/src/styles/neumorphism/input.scss new file mode 100644 index 00000000..ae590da6 --- /dev/null +++ b/src/styles/neumorphism/input.scss @@ -0,0 +1,49 @@ +$neu-input-border-width: 0 !default; +$neu-input-background: var(--neu-color-base-background) !default; +$neu-input-box-shadow: -1px -1px 1px rgba(0, 0, 0, 0.02), 1px 1px 3px rgba(0, 0, 0, 0.1), inset 1px 1px 2px rgba(255, 255, 255, 0.8) !default; +$neu-input-box-shadow-active: 1px 1px 2px #FFFFFF, inset 1px 1px 2px rgba(0, 0, 0, 0.1) !default; +$neu-input-color: var(--neu-color-base-content-primary) !default; +$neu-input-color-disabled: var(--neu-color-base-content-secondary) !default; +$neu-input-placeholder-color: var(--neu-color-base-content-secondary) !default; +$neu-input-placeholder-offset: -2px !default; +$neu-input-padding: 8px 16px !default; + +.s-input.neumorphic { + border-width: $neu-input-border-width; + box-shadow: $neu-input-box-shadow; + color: $neu-input-color; + padding: $neu-input-padding; + + .el-input, .el-textarea { + &__inner { + color: $neu-input-color; + } + + &.is-disabled { + .el-input__inner { + background-color: transparent; + } + } + } + + & .s-placeholder { + color: $neu-input-placeholder-color; + top: $neu-input-placeholder-offset; + } + + &, &:hover, &.s-focused, &.s-disabled { + background: $neu-input-background; + } + + &.s-focused, &.s-disabled { + box-shadow: $neu-input-box-shadow-active; + } + + &.s-disabled { + color: $neu-input-color-disabled; + + .s-placeholder { + color: $neu-input-placeholder-color; + } + } +} diff --git a/src/styles/neumorphism/variables.scss b/src/styles/neumorphism/variables.scss new file mode 100644 index 00000000..4cb6a9cc --- /dev/null +++ b/src/styles/neumorphism/variables.scss @@ -0,0 +1,70 @@ +// Brand +$neu-brand-pinky: #ED145B !default; // Some texts, brand stuff +$neu-brand-blue: #0D0248 !default; // Brand stuff +// Theme Primary +$neu-color-theme-accent: #F8087B !default; +$neu-color-theme-accent-hover: #F754A3 !default; +$neu-color-theme-accent-pressed: #E44592 !default; +$neu-color-theme-accent-focused: #F24197 !default; +// Theme Secondary +$neu-color-theme-secondary: #44E5B2 !default; +$neu-color-theme-secondary-hover: #24DAA0 !default; +// Base: Content +$neu-color-base-content-primary: #2A171F !default; // Main text and icons +$neu-color-base-content-secondary: #A19A9D !default; // Secondary text +$neu-color-base-content-tertiary: #D5CDD0 !default; // Hint and placeholder text +$neu-color-base-content-quaternary: #75787b !default; // Text +// Base: Background +$neu-color-base-background: #FAF4F8 !default; // Interactive elements background: controls, text field +$neu-color-base-background-hover: #F7F3F4 !default; +$neu-color-base-background-dark: #2E2E36 !default; // Dark background +// Base: Border +$neu-color-base-border-primary: #F7F3F4 !default; // High emphasis border/divider +$neu-color-base-border-secondary: #EDE4E7 !default; // Low emphasis border/divider +// Base: Misc +$neu-color-base-disabled: #FDF7FB !default; // Disabled state +$neu-color-base-on-disabled: #A19A9D !default; // Text on disabled element +$neu-color-base-on-accent: #FFFFFF !default; // Text on dark/accent backgrounds +// Utility +$neu-color-utility-body: #F7F3F4 !default; +$neu-color-utility-surface: #FDF7FB !default; +$neu-color-utility-overlay: rgba(0, 0, 0, 0.45) !default; +// Statuses +$neu-color-status-success: #34AD87 !default; // Success text and icon +$neu-color-status-success-background: #B9EBDB !default; // Success state background +$neu-color-status-warning: #479AEF !default; // Warning text and icon +$neu-color-status-warning-background: #C6E2FF !default; // Warning state background +$neu-color-status-error: #F754A3 !default; // Error text and icon +$neu-color-status-error-background: #FFD8EB !default; // Error state background + +:root { + --neu-brand-pinky: #{neu-brand-pinky}; + --neu-brand-blue: #{neu-brand-blue}; + --neu-color-theme-accent: #{$neu-color-theme-accent}; + --neu-color-theme-accent-hover: #{$neu-color-theme-accent-hover}; + --neu-color-theme-accent-pressed: #{$neu-color-theme-accent-pressed}; + --neu-color-theme-accent-focused: #{$neu-color-theme-accent-focused}; + --neu-color-theme-secondary: #{$neu-color-theme-secondary}; + --neu-color-theme-secondary-hover: #{$neu-color-theme-secondary-hover}; + --neu-color-base-content-primary: #{$neu-color-base-content-primary}; + --neu-color-base-content-secondary: #{$neu-color-base-content-secondary}; + --neu-color-base-content-tertiary: #{$neu-color-base-content-tertiary}; + --neu-color-base-content-quaternary: #{$neu-color-base-content-quaternary}; + --neu-color-base-background: #{$neu-color-base-background}; + --neu-color-base-background-hover: #{$neu-color-base-background-hover}; + --neu-color-base-background-dark: #{$neu-color-base-background-dark}; + --neu-color-base-border-primary: #{$neu-color-base-border-primary}; + --neu-color-base-border-secondary: #{$neu-color-base-border-secondary}; + --neu-color-base-disabled: #{$neu-color-base-disabled}; + --neu-color-base-on-disabled: #{$neu-color-base-on-disabled}; + --neu-color-base-on-accent: #{$neu-color-base-on-accent}; + --neu-color-utility-body: #{$neu-color-utility-body}; + --neu-color-utility-surface: #{$neu-color-utility-surface}; + --neu-color-utility-overlay: #{$neu-color-utility-overlay}; + --neu-color-status-success: #{$neu-color-status-success}; + --neu-color-status-success-background: #{$neu-color-status-success-background}; + --neu-color-status-warning: #{$neu-color-status-warning}; + --neu-color-status-warning-background: #{$neu-color-status-warning-background}; + --neu-color-status-error: #{$neu-color-status-error}; + --neu-color-status-error-background: #{$neu-color-status-error-background}; +} diff --git a/src/types/components.ts b/src/types/components.ts index 6be5fd3d..e5cd2b7f 100644 --- a/src/types/components.ts +++ b/src/types/components.ts @@ -12,6 +12,7 @@ export enum Components { SCollapseItem = 'SCollapseItem', SContainer = 'SContainer', SDatePicker = 'SDatePicker', + SDesignSystemProvider = 'SDesignSystemProvider', SDialog = 'SDialog', SDivider = 'SDivider', SDropdown = 'SDropdown', diff --git a/src/utils/DesignSystem.ts b/src/utils/DesignSystem.ts new file mode 100644 index 00000000..dd17844a --- /dev/null +++ b/src/utils/DesignSystem.ts @@ -0,0 +1,4 @@ +export enum DesignSystemTypes { + DEFAULT = '', + NEUMORPHIC = 'neumorphic' +} diff --git a/src/utils/index.ts b/src/utils/index.ts index f1551c93..1eed4d27 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,3 +4,7 @@ import { Theme } from './Theme' export const setTheme = (theme: Theme) => { store.dispatch('changeTheme', { theme }) } + +export const setDesignSystem = (designSystem) => { + store.dispatch('setDesignSystem', designSystem) +} diff --git a/yarn.lock b/yarn.lock index 89b86a7b..bf3b9dc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13383,10 +13383,10 @@ rollup-plugin-commonjs@^10.1.0: resolve "^1.11.0" rollup-pluginutils "^2.8.1" -rollup-plugin-copy@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz#5ba230047f86b9f703a29288f242948a5580e7b9" - integrity sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA== +rollup-plugin-copy@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz#f1228a3ffb66ffad8606e2f3fb7ff23141ed3286" + integrity sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ== dependencies: "@types/fs-extra" "^8.0.1" colorette "^1.1.0" From 5b4b64d15eb089bc49ade910fa64ce883916ec7a Mon Sep 17 00:00:00 2001 From: Nikita Polyakov <53777036+Nikita-Polyakov@users.noreply.github.com> Date: Tue, 1 Jun 2021 11:55:06 +0300 Subject: [PATCH 02/73] Release/0.9.1 (#209) * Neomorphism. Button. (#205) * add color variables * add provide inject components * fix inject design system * add primary secondary tertiary styles * add action and alternative states * remove duplicated styles * fefactoring neo css vars usage * fix ln * fix export * fix config * remove console.log * rename neo to neu * Neumorphism/input (#206) * wip neumorphic input * add neu input styles & refactoring * fix file input * export setDesignTheme * Update STooltip.stories.ts (#197) * fix build (#208) Co-authored-by: Alexander Yakimov Co-authored-by: Stefan Popov --- build/rollup.config.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/rollup.config.js b/build/rollup.config.js index 2d7a79bc..45d271ad 100644 --- a/build/rollup.config.js +++ b/build/rollup.config.js @@ -33,7 +33,7 @@ export default { targets: [ { src: 'src/assets/*', dest: 'lib/assets' }, { - src: 'src/styles/*.scss', + src: 'src/styles/*.(scss|ts)', dest: 'lib/styles', // Replace all imports for scss files which will be used as theming files transform: (content) => { diff --git a/package.json b/package.json index 23486122..fe051acd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@soramitsu/soramitsu-js-ui", - "version": "0.9.0", + "version": "0.9.1", "private": false, "publishConfig": { "registry": "https://nexus.iroha.tech/repository/npm-soramitsu/" From b3fffa541a5752c371abf34beae53373bb12d114 Mon Sep 17 00:00:00 2001 From: Nikita Polyakov <53777036+Nikita-Polyakov@users.noreply.github.com> Date: Wed, 2 Jun 2021 08:17:41 +0300 Subject: [PATCH 03/73] Release/0.9.2 (#211) * Neomorphism. Button. (#205) * add color variables * add provide inject components * fix inject design system * add primary secondary tertiary styles * add action and alternative states * remove duplicated styles * fefactoring neo css vars usage * fix ln * fix export * fix config * remove console.log * rename neo to neu * Neumorphism/input (#206) * wip neumorphic input * add neu input styles & refactoring * fix file input * export setDesignTheme * Update STooltip.stories.ts (#197) * fix build (#208) * neu styles & props improvement (#210) * neu styles & props improvement * fix mixin * up to 0.9.2 Co-authored-by: Alexander Yakimov Co-authored-by: Stefan Popov --- package.json | 2 +- src/components/Button/SButton.vue | 12 +++++++++--- src/components/Input/SFloatInput.vue | 6 +++++- src/components/Input/SInput.vue | 3 +++ src/stories/SButton.stories.ts | 14 ++++++++++---- src/stories/SFloatInput.stories.ts | 19 ++++++++++++++++++- src/styles/neumorphism/button.scss | 22 ++++++++++++++++++++++ src/styles/neumorphism/variables.scss | 9 +++++++++ 8 files changed, 77 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index fe051acd..4199f88a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@soramitsu/soramitsu-js-ui", - "version": "0.9.1", + "version": "0.9.2", "private": false, "publishConfig": { "registry": "https://nexus.iroha.tech/repository/npm-soramitsu/" diff --git a/src/components/Button/SButton.vue b/src/components/Button/SButton.vue index b4d15841..f7daf048 100644 --- a/src/components/Button/SButton.vue +++ b/src/components/Button/SButton.vue @@ -9,7 +9,7 @@ :disabled="disabled" :loading="isLoading" :autofocus="autofocus" - :circle="type === ButtonTypes.ACTION && rounded" + :circle="isRounded" :icon="elementIcon" @click="handleClick" > @@ -134,7 +134,7 @@ export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin, Design if ((Object.values(ButtonTypes) as Array).includes(this.type)) { cssClasses.push(`s-${this.type}`) } - if ((Object.values(ButtonIconPosition) as Array).includes(this.iconPosition)) { + if (!!this.availableIcon && (Object.values(ButtonIconPosition) as Array).includes(this.iconPosition)) { cssClasses.push(`s-i-position-${this.iconPosition}`) } if (this.isLoading) { @@ -169,8 +169,14 @@ export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin, Design return this.icon } + get isRounded (): boolean { + if (([ButtonTypes.LINK, ButtonTypes.ACTION] as Array).includes(this.type)) return false + + return this.rounded + } + get isLoading (): boolean { - if (([ButtonTypes.LINK, ButtonTypes.ACTION] as Array).includes(this.type)) { + if (([ButtonTypes.LINK, ButtonTypes.ACTION] as Array).includes(this.type) || this.isRounded) { return false } return this.loading diff --git a/src/components/Input/SFloatInput.vue b/src/components/Input/SFloatInput.vue index 5667791a..e4aab325 100644 --- a/src/components/Input/SFloatInput.vue +++ b/src/components/Input/SFloatInput.vue @@ -8,7 +8,11 @@ input: handleInput, blur: onBlur }" - /> + > + + diff --git a/src/components/Input/SInput.vue b/src/components/Input/SInput.vue index 2ac3d40f..7cd64228 100644 --- a/src/components/Input/SInput.vue +++ b/src/components/Input/SInput.vue @@ -9,7 +9,7 @@
- {{ placeholder }} + {{ placeholder }} { @@ -207,7 +207,7 @@ export default class SInput extends Mixins(BorderRadiusMixin, DesignSystemInject cssClasses.push('s-autofill') } if (this.size) { - cssClasses.push(this.sizeClass) + cssClasses.push(`s-size-${this.size}`) } if (this.prefix) { cssClasses.push('s-input--prefix') @@ -218,16 +218,6 @@ export default class SInput extends Mixins(BorderRadiusMixin, DesignSystemInject return cssClasses } - get sizeClass (): string { - switch (this.size) { - case InputSize.MEDIUM: - return 's-size-medium' - case InputSize.BIG: - default: - return '' - } - } - get computedType (): string { if (this.type === InputType.TEXT_FILE) { return InputType.TEXT diff --git a/src/components/Input/consts.ts b/src/components/Input/consts.ts index bb48a246..82796e87 100644 --- a/src/components/Input/consts.ts +++ b/src/components/Input/consts.ts @@ -31,6 +31,7 @@ export enum InputType { } export enum InputSize { + SMALL = 'small', MEDIUM = 'medium', BIG = 'big' } diff --git a/src/components/Radio/SRadio.vue b/src/components/Radio/SRadio.vue index ff62d7d5..62ed9721 100644 --- a/src/components/Radio/SRadio.vue +++ b/src/components/Radio/SRadio.vue @@ -1,5 +1,6 @@ diff --git a/src/components/Switch/SSwitch.vue b/src/components/Switch/SSwitch.vue index e8b85bc4..1b220064 100644 --- a/src/components/Switch/SSwitch.vue +++ b/src/components/Switch/SSwitch.vue @@ -1,5 +1,5 @@ + + +``` + +Tree shaking relies on ES module imports. Keep bundlers configured for ESM (`moduleResolution: "bundler"` in TypeScript, Vite build in ESM mode). + +### Styles + +All components share a single CSS bundle emitted as `@soramitsu-ui/ui/styles`. Import it once at the top of your project or in the plugin entry point. The CSS bundle injects CSS variables and resets sourced from `@soramitsu-ui/theme` tokens. + +## Component catalogue + +| Category | Components | +| --- | --- | +| Inputs | `SCheckbox`, `SRadio`, `SSwitch`, `SSelect`, `STextField`, `SDatePicker`, `SJsonInput` | +| Navigation | `SNavigationMenu`, `SNavigationSubmenu`, `SNavigationMenuItem`, `STabs`, `SLink` | +| Feedback | `SAlert`, `SBadge`, `SProgressBar`, `SSpinner`, `SNotifications`, `SToasts` | +| Surfaces | `SAccordion`, `SModal`, `SPopover`, `STable`, `SCard`-style layouts (table adapt mode) | +| Utilities | `SBodyScrollLockProvider`, `STooltip`, transition wrappers in `Transitions/` | + +Storybook documents the full API surface with live examples. Use `yarn sb:serve` and navigate through component categories. + +## API conventions + +- **Props are typed via TypeScript interfaces** (`types.ts` per component). Keep props flat and serialisable; prefer objects for complex configuration. +- **Events follow the `action:noun` pattern** (`click:row`, `update:modelValue`, `change:expand`). Avoid hyphenated legacy names when adding new events. +- **Slots mirror the DOM structure** (e.g. `header`, `row`, `details`, `prefix`). When adding slots, document default renderers in the Storybook stories. +- **Class names use BEM with underscores** (`button__icon_size_small`). Reuse existing modifiers before adding new ones. +- **Accessibility first.** Components ship with ARIA attributes, focus management, and keyboard interactions. When editing them: + - Preserve tab order. + - Keep `aria-*` attributes in sync with state. + - Cover focus-trap behaviour in Cypress tests when toggling modals, popovers, and dropdowns. + +## Provide/inject contracts + +Complex components such as Checkbox groups, Navigation menus, and Tabs provide a context via `provide/inject`. Shared contracts live in `api.ts` inside the component directory. When extending APIs: + +1. Update the provided context type and default object. +2. Consume the context via a dedicated composable (e.g. `useCheckboxGroupApi`). +3. Document required provider usage in Storybook and this guide. + +## Data attributes for testing + +Cypress component tests target `data-testid` attributes. Keep them stable and descriptive: + +```vue + +``` + +If you rename a `data-testid`, update the corresponding spec under `packages/ui/cypress/component/`. + +## Writing a new component + +1. **Scaffold the directory** under `packages/ui/src/components/` and create an `S.vue` file. +2. **Export it** from `index.ts` inside the component folder, then surface it through `packages/ui/src/components/index.ts` and `all-components.ts`. +3. **Author a Storybook story** in `packages/ui/stories/components/.stories.ts` to showcase common states, edge cases, and accessibility usage. +4. **Write Cypress component tests** under `packages/ui/cypress/component/.spec.cy.ts`. Focus on keyboard interaction, focus traps, and visual state toggles. +5. **Add unit tests** for complex utilities or composables in the component folder (`*.spec.ts`). +6. **Lint and type-check** using `yarn lint:check` and `yarn --cwd packages/ui typecheck`. +7. **Update API Extractor output** by running `yarn --cwd packages/ui build:tsc` followed by `yarn --cwd packages/ui api:extract:local`. + +## Composables and utilities + +`packages/ui/src/composables` and `packages/ui/src/util` host sharable logic such as: + +- `useClickOutside` +- `useTrapFocus` +- `useDimensions` +- Date and formatting helpers + +When adding new composables: + +- Prefix the file with `use`. +- Export both the named composable and the inferred return type. +- Cover tricky logic with Vitest specs (`packages/ui/src/composables/__tests__`). +- Document their intended use in component stories when they alter behaviour. + +## Icons + +The `icons` directory under the UI components is reserved for icon wrappers. Most implementations delegate to the `@soramitsu-ui/icons` package. When adding icons: + +- Add the SVG to `packages/icons` (if it does not exist yet). +- Create a Vue wrapper in `packages/ui/src/components/icons` for consistent sizing and loading. +- Update `packages/ui/src/components/icons/index.ts` to export new icons. + +## Accessibility checklist + +Before shipping a component change: + +- Verify keyboard navigation paths in Storybook. +- Run Cypress specs with `yarn --cwd packages/ui cy:ci:component` (CI uses the same command). +- Use `cypress-axe` or browser plugins to scan for WAI-ARIA violations. +- Document accessible usage in the corresponding story (e.g. labelling inputs, describing popovers). + +## Integration tips + +- Keep consumer apps aligned with the same Vue version as listed in `packages/ui/package.json` (`^3.5.21`). +- When using `pinia` or other peer dependencies in stories, import them inside Storybook preview files to avoid leaking peers into the component bundle. +- For SSR projects, import CSS in the entry server file and avoid DOM-only APIs in component setup (gate them behind `if (process.client)` checks or use `onMounted`). + +Refer back to this guide whenever you introduce API changes to maintain consistency across the library. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 00000000..ddf513a8 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,63 @@ +# Contributing + +We welcome contributions that enhance the Soramitsu UI library. This guide captures expectations for collaboration, code style, and review. + +## Communication + +- Discuss major features or breaking changes with maintainers before implementation. +- Use GitHub issues for bugs and feature requests; cross-link related Storybook stories or Figma files. +- Mention relevant stakeholders (design, QA) when changes impact them. + +## Coding standards + +- Follow the [components.md](components.md) conventions for Vue architecture, naming, and accessibility. +- Adhere to the existing TypeScript config; avoid introducing alternative build systems without discussion. +- Keep files ASCII-encoded unless existing files use extended characters. + +## Commit and Changeset messages + +- Commits can be descriptive (`fix: align tooltip with trigger`), but releases rely on Changesets for user-facing notes. +- Changeset format: `**type**(scope): message` (e.g. `**fix**(SSelect): avoid double focus trap`). +- Include breaking change notices in the Changeset body and in Storybook docs. + +## Documentation updates + +- Update this documentation suite whenever behaviour or workflows change. +- Link to source files in doc updates (e.g. `packages/ui/src/components/Table/STable.vue:42`). +- Keep Storybook stories in sync with component APIs. + +## Code review etiquette + +- Request review from at least one maintainer and one peer familiar with the affected area. +- Provide context: describe motivation, highlight risky areas, attach screenshots or videos for visual tweaks. +- Address review feedback promptly or start a discussion if trade-offs are involved. + +## Testing expectations + +- Run targeted tests locally; CI runs `yarn test:all`. +- For UI behaviour changes, include or update Cypress specs. +- Capture unusual failure cases in tests rather than comments. + +## Accessibility and internationalisation + +- Ensure keyboard navigation and focus order remain consistent. +- Provide default English copy only when a string is essential; otherwise expose props/slots for localisation. +- Use semantic HTML elements whenever possible. + +## Triaging issues + +- Confirm reported bugs by reproducing them in Storybook. +- Label issues with affected components and severity. +- Link to the relevant entry in [backlog.md](backlog.md) if it already tracks the gap. + +## Security + +- Do not include secrets in commits (API keys, tokens). +- Report security concerns privately to the maintainers before opening public issues. + +## Contributor licence + +- Contributions are licensed under Apache 2.0, consistent with the repository licence. +- Ensure third-party code complies with compatible licences before inclusion. + +Thank you for helping keep the Soramitsu design system robust and accessible. Raise a discussion if any part of this process can be improved—processes evolve alongside the codebase. diff --git a/docs/development-workflow.md b/docs/development-workflow.md new file mode 100644 index 00000000..d525275b --- /dev/null +++ b/docs/development-workflow.md @@ -0,0 +1,90 @@ +# Development Workflow + +This guide captures the day-to-day routines for contributing to the Soramitsu UI monorepo. + +## Branch hygiene + +- Prefer `feat/`, `fix/`, or `chore/` naming. +- Keep branches focused; split large features into incremental pull requests when possible. +- Rebase onto the default branch before requesting review to avoid merge commits in history. + +## Editor setup + +- Enable ESLint and Prettier integration. For VS Code, install **Prettier ESLint** and configure "Format on Save" to call `./node_modules/.bin/prettier-eslint`. +- Enable Volar or Vetur for Vue 3 single-file component support. +- Configure TypeScript to use the workspace version (`cmd + shift + P` → "TypeScript: Select TypeScript Version" → "Use Workspace Version"). + +## Local development loop + +1. **Install dependencies** (once per clone): `yarn`. +2. **Run Storybook** when working on component visuals: `yarn sb:serve`. +3. **Launch Vitest in watch mode** for instant feedback: + ```sh + yarn --cwd packages/ui test:unit --watch + ``` +4. **Start Cypress component tests** interactively if you change behaviour reliant on the DOM or keyboard interactions: + ```sh + yarn --cwd packages/ui cy + ``` +5. **Update theme builds** whenever you touch tokens: `yarn build:theme`. + +## Linting and formatting + +- Quick format fix: `yarn lint:format:fix` +- ESLint only: `yarn lint:es` +- Combined check (CI equivalent): `yarn lint:check` + +Ensure lint commands run clean before pushing. Prettier and ESLint rules are shared across packages to keep code style uniform. + +## Type checking + +Run type checks after significant TypeScript or Vue composable changes: + +```sh +yarn --cwd packages/ui typecheck +``` + +CI runs this step implicitly via `yarn test:all`; catching issues locally speeds up review. + +## API Extractor updates + +When adding or modifying public component APIs: + +```sh +yarn --cwd packages/ui build:tsc +yarn --cwd packages/ui api:extract:local +``` + +Commit the resulting `ui.api.md` diff with the feature branch. + +## Storybook maintenance + +- Keep stories light; prefer controls over hard-coded variants. +- Document breaking changes or new props through Storybook docs panels. +- Run `yarn --cwd packages/ui sb:build` to ensure the static build passes before merging significant UI updates. + +## Changesets + +Every user-facing change (new component, breaking change, bug fix) should include a Changeset: + +```sh +yarn changeset +``` + +Follow the prompt, using the `**type**(scope): message` format described in `README.md`. + +## Pull request checklist + +- [ ] Linting and formatting pass locally. +- [ ] `yarn test:all` succeeds or any failing step is explained in the PR description. +- [ ] Stories, tests, and docs updated alongside code changes. +- [ ] API Extractor output updated when public APIs change. +- [ ] Screenshots or GIFs attached for visual updates where helpful. + +## Reviewing others' work + +- Pull the branch locally when visual changes are involved; run Storybook to verify. +- Check Cypress and Vitest snapshots for changes caused by the patch. +- Cross-reference `backlog.md` to avoid regressing known gaps. + +Consistency and automation keep the design system reliable. Adapt this workflow as tooling evolves and document updates here. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 00000000..8893fe46 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,112 @@ +# Getting Started + +Follow this guide to set up a local environment that can build, test, and publish Soramitsu UI packages. It covers both consumers (who install published packages) and contributors (who clone the monorepo). + +## Prerequisites + +- **Node.js 18 LTS or newer.** Earlier versions miss APIs used by Vite and Vitest. Verify with `node --version`. +- **Yarn Classic (1.x).** The repository relies on workspaces; the `yarn.lock` is managed by Yarn 1. Install via `npm i -g yarn` if required. +- **Git LFS** for large assets (optional today, but recommended for future design assets). +- **Modern browser** (Chromium or Firefox) for Storybook and Cypress component testing. + +> Tip: Use [Volta](https://volta.sh/) or [nvm](https://github.com/nvm-sh/nvm) to pin Node versions per project. + +## Clone and install + +```sh +# Clone the repository + git clone git@github.com:soramitsu/soramitsu-js-ui-library.git + cd soramitsu-js-ui-library + +# Install workspace dependencies + yarn +``` + +The install command resolves all workspace dependencies (including Storybook, Vite, Cypress, and token tooling). Expect the first install to take several minutes. + +## Bootstrap the toolchain + +Some workflows depend on generated artefacts: + +1. **Build the theme tokens at least once per session.** + ```sh + yarn build:theme + ``` + This compiles Sass tokens into CSS variables consumed by the UI package. + +2. **Build the SVG plugin** (runs automatically before Storybook via `presb:serve`, but it can be invoked manually when testing changes to the plugin). + ```sh + yarn build:vite-plugin-svg + ``` + +3. **Run Storybook** to explore components interactively. + ```sh + yarn sb:serve + ``` + Storybook is served from `packages/ui/.storybook` and listens on port 6006 by default. + +4. **(Optional) Compile all packages.** Useful for ensuring release readiness. + ```sh + yarn build + ``` + +## Working on a single package + +Each package ships with its own scripts and configuration located under `packages/`. + +- **UI components:** `yarn --cwd packages/ui