mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-11-13 07:48:26 +03:00
commit
f89cbe6968
@ -1,3 +1,5 @@
|
||||
Nri.Ui.BreadCrumbs.V1,upgrade to V2
|
||||
Nri.Ui.Checkbox.V5,upgrade to V6
|
||||
Nri.Ui.InputStyles.V3,upgrade to V4
|
||||
Nri.Ui.Tabs.V6,upgrade to V7
|
||||
Nri.Ui.TextArea.V4,upgrade to V5
|
||||
|
|
2
elm.json
2
elm.json
@ -40,6 +40,7 @@
|
||||
"Nri.Ui.Html.Attributes.V2",
|
||||
"Nri.Ui.Html.V3",
|
||||
"Nri.Ui.InputStyles.V3",
|
||||
"Nri.Ui.InputStyles.V4",
|
||||
"Nri.Ui.Loading.V1",
|
||||
"Nri.Ui.Logo.V1",
|
||||
"Nri.Ui.MasteryIcon.V1",
|
||||
@ -69,6 +70,7 @@
|
||||
"Nri.Ui.Text.V6",
|
||||
"Nri.Ui.Text.Writing.V1",
|
||||
"Nri.Ui.TextArea.V4",
|
||||
"Nri.Ui.TextArea.V5",
|
||||
"Nri.Ui.TextInput.V7",
|
||||
"Nri.Ui.Tooltip.V3",
|
||||
"Nri.Ui.UiIcon.V1"
|
||||
|
@ -68,6 +68,10 @@ hint = 'upgrade to V5'
|
||||
[forbidden."Nri.Ui.InputStyles.V2"]
|
||||
hint = 'upgrade to V3'
|
||||
|
||||
[forbidden."Nri.Ui.InputStyles.V3"]
|
||||
hint = 'upgrade to V4'
|
||||
usages = ['styleguide/../src/Nri/Ui/TextArea/V4.elm']
|
||||
|
||||
[forbidden."Nri.Ui.Menu.V1"]
|
||||
hint = 'upgrade to V3'
|
||||
|
||||
@ -163,6 +167,9 @@ usages = [
|
||||
[forbidden."Nri.Ui.Text.V5"]
|
||||
hint = 'upgrade to V6'
|
||||
|
||||
[forbidden."Nri.Ui.TextArea.V4"]
|
||||
hint = 'upgrade to V5'
|
||||
|
||||
[forbidden."Nri.Ui.TextInput.V6"]
|
||||
hint = 'upgrade to V7'
|
||||
|
||||
|
67
lib/TextArea/V5.js
Normal file
67
lib/TextArea/V5.js
Normal file
@ -0,0 +1,67 @@
|
||||
CustomElement = require("../CustomElement");
|
||||
|
||||
CustomElement.create({
|
||||
tagName: "nri-textarea-v45",
|
||||
|
||||
initialize: function () {
|
||||
this._autoresize = false;
|
||||
},
|
||||
|
||||
onConnect: function () {
|
||||
this._textarea = this.querySelector("textarea");
|
||||
this._updateListener();
|
||||
},
|
||||
|
||||
observedAttributes: ["data-autoresize"],
|
||||
|
||||
onAttributeChange: function (name, previous, next) {
|
||||
if (name === "data-autoresize") {
|
||||
this._autoresize = next !== null;
|
||||
if (!this._textarea) return;
|
||||
this._updateListener();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
_updateListener: function () {
|
||||
if (this._autoresize) {
|
||||
this._textarea.addEventListener("input", this._resize);
|
||||
this._resize();
|
||||
} else {
|
||||
this._textarea.removeEventListener("input", this._resize);
|
||||
}
|
||||
},
|
||||
|
||||
_resize: function () {
|
||||
var minHeight = null;
|
||||
var computedStyles = window.getComputedStyle(this._textarea);
|
||||
|
||||
if (this._textarea.style.minHeight) {
|
||||
minHeight = parseInt(this._textarea.style.minHeight, 10);
|
||||
} else {
|
||||
minHeight = parseInt(computedStyles.minHeight, 10);
|
||||
}
|
||||
|
||||
if (minHeight === 0) {
|
||||
minHeight = parseInt(computedStyles.height, 10);
|
||||
}
|
||||
|
||||
this._textarea.style.overflowY = "hidden";
|
||||
this._textarea.style.minHeight = minHeight + "px";
|
||||
this._textarea.style.transition = "none";
|
||||
|
||||
// the browser does not include border widths in `.scrollHeight`, but we
|
||||
// sometimes use `box-sizing: border-box` on these elements so we need to
|
||||
// take it into account when setting the CSS `height`.
|
||||
var borderOffset = 0;
|
||||
if (computedStyles.boxSizing === "border-box") {
|
||||
borderOffset =
|
||||
parseInt(computedStyles.borderTopWidth, 10) +
|
||||
parseInt(computedStyles.borderBottomWidth, 10);
|
||||
}
|
||||
|
||||
this._textarea.style.height =
|
||||
Math.max(minHeight, this._textarea.scrollHeight + borderOffset) + "px";
|
||||
},
|
||||
},
|
||||
});
|
@ -1,3 +1,4 @@
|
||||
require("./TextArea/V4");
|
||||
require("./TextArea/V5");
|
||||
|
||||
exports.CustomElement = require("./CustomElement");
|
||||
|
@ -12,7 +12,7 @@ import Css
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import InputErrorAndGuidanceInternal exposing (ErrorState)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.InputStyles.V3 as InputStyles exposing (Theme)
|
||||
import Nri.Ui.InputStyles.V4 as InputStyles exposing (Theme)
|
||||
|
||||
|
||||
{-| -}
|
||||
|
@ -20,7 +20,7 @@ import Css exposing (Color)
|
||||
import Css.Global exposing (Snippet)
|
||||
import Nri.Ui.Colors.Extra exposing (toCssString)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.InputStyles.V3 as InputStyles exposing (focusedErrorInputBoxShadow, focusedInputBoxShadow)
|
||||
import Nri.Ui.InputStyles.V4 as InputStyles exposing (focusedErrorInputBoxShadow, focusedInputBoxShadow)
|
||||
|
||||
|
||||
{-| When :focus-visible, add the two-tone focus ring.
|
||||
|
267
src/Nri/Ui/InputStyles/V4.elm
Normal file
267
src/Nri/Ui/InputStyles/V4.elm
Normal file
@ -0,0 +1,267 @@
|
||||
module Nri.Ui.InputStyles.V4 exposing
|
||||
( label, Theme(..), input
|
||||
, inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight, defaultMarginTop
|
||||
, focusedInputBoxShadow, focusedErrorInputBoxShadow, errorClass, inputClass
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
### Changes from V3
|
||||
|
||||
- Remove ContentCreation theme
|
||||
|
||||
|
||||
### Patch changes
|
||||
|
||||
- expose defaultMarginTop
|
||||
|
||||
|
||||
### Changes from V2
|
||||
|
||||
- adds UserGenerated
|
||||
|
||||
InputStyles used by the TextInput and TextArea widgets.
|
||||
|
||||
@docs label, Theme, input
|
||||
|
||||
|
||||
## Shared hardcoded values
|
||||
|
||||
@docs inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight, defaultMarginTop
|
||||
@docs focusedInputBoxShadow, focusedErrorInputBoxShadow, errorClass, inputClass
|
||||
|
||||
-}
|
||||
|
||||
import Css exposing (..)
|
||||
import Css.Global
|
||||
import Nri.Ui.Colors.Extra as ColorsExtra
|
||||
import Nri.Ui.Colors.V1 exposing (..)
|
||||
import Nri.Ui.Fonts.V1
|
||||
|
||||
|
||||
{-| -}
|
||||
type Theme
|
||||
= Standard
|
||||
| UserGenerated
|
||||
| Writing
|
||||
|
||||
|
||||
{-| -}
|
||||
label : Theme -> Bool -> Style
|
||||
label theme inError =
|
||||
let
|
||||
sharedStyles =
|
||||
batch
|
||||
[ backgroundColor white
|
||||
, left (px 10)
|
||||
, top zero
|
||||
, fontSize (px 12)
|
||||
, Nri.Ui.Fonts.V1.baseFont
|
||||
, position absolute
|
||||
, fontWeight (int 600)
|
||||
, borderRadius (px 4)
|
||||
, property "transition" "all 0.4s ease"
|
||||
]
|
||||
in
|
||||
case theme of
|
||||
Standard ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 (px 2) (px 5)
|
||||
, fontSize (px 12)
|
||||
, color navy
|
||||
, if inError then
|
||||
batch [ color purple ]
|
||||
|
||||
else
|
||||
batch []
|
||||
]
|
||||
|
||||
UserGenerated ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 zero (px 5)
|
||||
, fontSize (px 12)
|
||||
, color navy
|
||||
, if inError then
|
||||
batch [ color purple ]
|
||||
|
||||
else
|
||||
batch []
|
||||
]
|
||||
|
||||
Writing ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 zero (px 5)
|
||||
, border3 (px 1) solid gray75
|
||||
, borderRadius (px 4)
|
||||
, fontSize (px 15)
|
||||
, color navy
|
||||
, if inError then
|
||||
batch
|
||||
[ color purple
|
||||
, backgroundColor white
|
||||
, borderColor purple
|
||||
]
|
||||
|
||||
else
|
||||
batch []
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
defaultMarginTop : Float
|
||||
defaultMarginTop =
|
||||
9
|
||||
|
||||
|
||||
{-| -}
|
||||
focusedInputBoxShadow : String
|
||||
focusedInputBoxShadow =
|
||||
"inset 0 3px 0 0 " ++ ColorsExtra.toCssString glacier
|
||||
|
||||
|
||||
{-| -}
|
||||
focusedErrorInputBoxShadow : String
|
||||
focusedErrorInputBoxShadow =
|
||||
"inset 0 3px 0 0 " ++ ColorsExtra.toCssString purpleLight
|
||||
|
||||
|
||||
{-| -}
|
||||
inputClass : String
|
||||
inputClass =
|
||||
"nri-input"
|
||||
|
||||
|
||||
{-| -}
|
||||
errorClass : String
|
||||
errorClass =
|
||||
"nri-input-error"
|
||||
|
||||
|
||||
{-| In order to use these styles in an input module, you will need to add the class "override-sass-styles". This is because sass styles in the monolith have higher precendence than the class styles here.
|
||||
-}
|
||||
input : Theme -> Style
|
||||
input theme =
|
||||
let
|
||||
sharedStyles =
|
||||
batch
|
||||
[ border3 (px 1) solid gray75
|
||||
, width (pct 100)
|
||||
, borderRadius (px 8)
|
||||
, pseudoClass "placeholder"
|
||||
[ color gray45
|
||||
]
|
||||
, color gray20
|
||||
|
||||
-- fix bootstrap
|
||||
, display inlineBlock
|
||||
, verticalAlign top
|
||||
, marginBottom zero
|
||||
, marginTop (px defaultMarginTop)
|
||||
, boxShadow6 inset zero (px 3) zero zero gray92
|
||||
, property "transition" "border-color 0.4s ease"
|
||||
, boxSizing borderBox
|
||||
, focus
|
||||
[ borderColor azure
|
||||
, outline none
|
||||
, property "box-shadow" focusedInputBoxShadow
|
||||
]
|
||||
, Css.Global.withClass errorClass
|
||||
[ borderColor purple
|
||||
, boxShadow6 inset zero (px 3) zero zero purpleLight
|
||||
, focus
|
||||
[ borderColor purple
|
||||
, property "box-shadow" focusedErrorInputBoxShadow
|
||||
]
|
||||
]
|
||||
]
|
||||
in
|
||||
batch
|
||||
[ Css.Global.withClass "override-sass-styles"
|
||||
[ case theme of
|
||||
Standard ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 inputPaddingVertical (px 15)
|
||||
, fontSize (px 15)
|
||||
, Nri.Ui.Fonts.V1.baseFont
|
||||
]
|
||||
|
||||
UserGenerated ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 inputPaddingVertical (px 15)
|
||||
, fontSize (px 15)
|
||||
, Nri.Ui.Fonts.V1.ugFont
|
||||
]
|
||||
|
||||
Writing ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, Nri.Ui.Fonts.V1.quizFont
|
||||
, fontSize (px 18)
|
||||
, lineHeight writingLineHeight
|
||||
, padding writingPadding
|
||||
, paddingTop writingPaddingTop
|
||||
, focus
|
||||
[ Css.Global.adjacentSiblings
|
||||
[ Css.Global.label
|
||||
[ backgroundColor azure
|
||||
, color white
|
||||
, borderColor azure
|
||||
, Css.Global.withClass errorClass
|
||||
[ backgroundColor purple
|
||||
, color white
|
||||
, borderColor purple
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
inputPaddingVertical : Px
|
||||
inputPaddingVertical =
|
||||
px 12
|
||||
|
||||
|
||||
{-| -}
|
||||
inputLineHeight : Px
|
||||
inputLineHeight =
|
||||
px 21
|
||||
|
||||
|
||||
{-| -}
|
||||
textAreaHeight : Px
|
||||
textAreaHeight =
|
||||
px 100
|
||||
|
||||
|
||||
{-| -}
|
||||
writingLineHeight : Px
|
||||
writingLineHeight =
|
||||
px 27
|
||||
|
||||
|
||||
{-| -}
|
||||
writingPadding : Px
|
||||
writingPadding =
|
||||
px 15
|
||||
|
||||
|
||||
{-| -}
|
||||
writingPaddingTop : Px
|
||||
writingPaddingTop =
|
||||
px 20
|
||||
|
||||
|
||||
{-| -}
|
||||
writingMinHeight : Px
|
||||
writingMinHeight =
|
||||
px 150
|
@ -57,7 +57,7 @@ import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.CssVendorPrefix.V1 as VendorPrefixed
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Html.Attributes.V2 as Extra
|
||||
import Nri.Ui.InputStyles.V3 as InputStyles
|
||||
import Nri.Ui.InputStyles.V4 as InputStyles
|
||||
import Nri.Ui.Util
|
||||
import SolidColor
|
||||
|
||||
|
456
src/Nri/Ui/TextArea/V5.elm
Normal file
456
src/Nri/Ui/TextArea/V5.elm
Normal file
@ -0,0 +1,456 @@
|
||||
module Nri.Ui.TextArea.V5 exposing
|
||||
( view, generateId
|
||||
, Attribute
|
||||
, value
|
||||
, onInput, onBlur
|
||||
, hiddenLabel, visibleLabel
|
||||
, css, noMargin
|
||||
, standard, writing
|
||||
, autoResize, autoResizeSingleLine
|
||||
, custom, nriDescription, id, testId
|
||||
, placeholder, autofocus
|
||||
, disabled, errorIf, errorMessage, guidance
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
## Changelog
|
||||
|
||||
|
||||
### Changes from V4
|
||||
|
||||
- Removes contentCreation view styles
|
||||
- Changes to a list-based API
|
||||
- Adds guidance and errorMessage support
|
||||
- Adds id, custom, nriDescription, testId, css, and noMargin
|
||||
- Adds disabled support
|
||||
|
||||
|
||||
## The next version of TextArea should:
|
||||
|
||||
- update the disabled styles
|
||||
|
||||
|
||||
## Upgrading to V4
|
||||
|
||||
- Adds field for onBlur
|
||||
|
||||
|
||||
## The Nri styleguide-specified textarea with overlapping label
|
||||
|
||||
|
||||
## Creating New Versions
|
||||
|
||||
When upgrading this module, we need to make sure to also include a new
|
||||
custom element, or else autosizing will break! This means doing the following:
|
||||
|
||||
1. Creating a new module in `lib/TextArea`
|
||||
2. Requiring that module in `lib/index.js`
|
||||
|
||||
|
||||
## API
|
||||
|
||||
@docs view, generateId
|
||||
@docs Attribute
|
||||
@docs value
|
||||
|
||||
|
||||
### Event handlers
|
||||
|
||||
@docs onInput, onBlur
|
||||
|
||||
|
||||
### Visual behavior
|
||||
|
||||
@docs hiddenLabel, visibleLabel
|
||||
@docs css, noMargin
|
||||
@docs standard, writing
|
||||
@docs autoResize, autoResizeSingleLine
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
@docs custom, nriDescription, id, testId
|
||||
@docs placeholder, autofocus
|
||||
@docs disabled, errorIf, errorMessage, guidance
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled.Aria as Aria
|
||||
import Css exposing (px)
|
||||
import Html.Styled as Html exposing (Html)
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import Html.Styled.Events as Events
|
||||
import InputErrorAndGuidanceInternal exposing (ErrorState, Guidance)
|
||||
import InputLabelInternal
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Html.Attributes.V2 as Extra
|
||||
import Nri.Ui.InputStyles.V4 as InputStyles exposing (Theme(..))
|
||||
import Nri.Ui.Util exposing (dashify, removePunctuation)
|
||||
|
||||
|
||||
{-| This is private. The public API only exposes `Attribute`.
|
||||
-}
|
||||
type alias Config msg =
|
||||
{ theme : Theme
|
||||
, guidance : Guidance
|
||||
, error : ErrorState
|
||||
, hideLabel : Bool
|
||||
, value : String
|
||||
, autofocus : Bool
|
||||
, onInput : Maybe (String -> msg)
|
||||
, onBlur : Maybe msg
|
||||
, placeholder : Maybe String
|
||||
, noMarginTop : Bool
|
||||
, containerCss : List Css.Style
|
||||
, custom : List (Html.Attribute Never)
|
||||
, id : Maybe String
|
||||
, height : HeightBehavior
|
||||
, disabled : Bool
|
||||
}
|
||||
|
||||
|
||||
defaultConfig : Config msg
|
||||
defaultConfig =
|
||||
{ theme = Standard
|
||||
, guidance = InputErrorAndGuidanceInternal.noGuidance
|
||||
, error = InputErrorAndGuidanceInternal.noError
|
||||
, hideLabel = False
|
||||
, value = ""
|
||||
, autofocus = False
|
||||
, onInput = Nothing
|
||||
, onBlur = Nothing
|
||||
, placeholder = Nothing
|
||||
, noMarginTop = False
|
||||
, containerCss = []
|
||||
, custom = []
|
||||
, id = Nothing
|
||||
, height = Fixed
|
||||
, disabled = False
|
||||
}
|
||||
|
||||
|
||||
applyConfig : List (Attribute msg) -> Config msg
|
||||
applyConfig =
|
||||
List.foldl (\(Attribute update) config -> update config) defaultConfig
|
||||
|
||||
|
||||
{-| Customizations for the TextArea.
|
||||
-}
|
||||
type Attribute msg
|
||||
= Attribute (Config msg -> Config msg)
|
||||
|
||||
|
||||
{-| Control whether to auto-expand the height.
|
||||
-}
|
||||
type HeightBehavior
|
||||
= Fixed
|
||||
| AutoResize Height
|
||||
|
||||
|
||||
{-| For specifying the actual height.
|
||||
-}
|
||||
type Height
|
||||
= DefaultHeight
|
||||
| SingleLine
|
||||
|
||||
|
||||
{-| -}
|
||||
autoResize : Attribute msg
|
||||
autoResize =
|
||||
Attribute (\soFar -> { soFar | height = AutoResize DefaultHeight })
|
||||
|
||||
|
||||
{-| -}
|
||||
autoResizeSingleLine : Attribute msg
|
||||
autoResizeSingleLine =
|
||||
Attribute (\soFar -> { soFar | height = AutoResize SingleLine })
|
||||
|
||||
|
||||
{-| -}
|
||||
value : String -> Attribute msg
|
||||
value value_ =
|
||||
Attribute (\soFar -> { soFar | value = value_ })
|
||||
|
||||
|
||||
{-| If no explicit placeholder is given, the input label will be used as the placeholder.
|
||||
-}
|
||||
placeholder : String -> Attribute msg
|
||||
placeholder text_ =
|
||||
Attribute (\soFar -> { soFar | placeholder = Just text_ })
|
||||
|
||||
|
||||
{-| This disables the textarea.
|
||||
-}
|
||||
disabled : Attribute msg
|
||||
disabled =
|
||||
Attribute (\config -> { config | disabled = True })
|
||||
|
||||
|
||||
{-| Sets whether or not the field will be highlighted as having a validation error.
|
||||
-}
|
||||
errorIf : Bool -> Attribute msg
|
||||
errorIf =
|
||||
Attribute << InputErrorAndGuidanceInternal.setErrorIf
|
||||
|
||||
|
||||
{-| If `Just`, the field will be highlighted as having a validation error,
|
||||
and the given error message will be shown.
|
||||
-}
|
||||
errorMessage : Maybe String -> Attribute msg
|
||||
errorMessage =
|
||||
Attribute << InputErrorAndGuidanceInternal.setErrorMessage
|
||||
|
||||
|
||||
{-| A guidance message shows below the input, unless an error message is showing instead.
|
||||
-}
|
||||
guidance : String -> Attribute msg
|
||||
guidance =
|
||||
Attribute << InputErrorAndGuidanceInternal.setGuidance
|
||||
|
||||
|
||||
{-| Hides the visible label. (There will still be an invisible label for screen readers.)
|
||||
-}
|
||||
hiddenLabel : Attribute msg
|
||||
hiddenLabel =
|
||||
Attribute (\soFar -> { soFar | hideLabel = True })
|
||||
|
||||
|
||||
{-| Default behavior.
|
||||
-}
|
||||
visibleLabel : Attribute msg
|
||||
visibleLabel =
|
||||
Attribute (\soFar -> { soFar | hideLabel = False })
|
||||
|
||||
|
||||
{-| Produce the given `msg` when the field is focused.
|
||||
-}
|
||||
onInput : (String -> msg) -> Attribute msg
|
||||
onInput msg =
|
||||
Attribute (\soFar -> { soFar | onInput = Just msg })
|
||||
|
||||
|
||||
{-| Produce the given `msg` when the field is blurred.
|
||||
-}
|
||||
onBlur : msg -> Attribute msg
|
||||
onBlur msg =
|
||||
Attribute (\soFar -> { soFar | onBlur = Just msg })
|
||||
|
||||
|
||||
{-| Sets the `autofocus` attribute of the textarea to true.
|
||||
-}
|
||||
autofocus : Attribute msg
|
||||
autofocus =
|
||||
Attribute (\soFar -> { soFar | autofocus = True })
|
||||
|
||||
|
||||
{-| Adds CSS to the element containing the textarea.
|
||||
-}
|
||||
css : List Css.Style -> Attribute msg
|
||||
css styles =
|
||||
Attribute (\soFar -> { soFar | containerCss = soFar.containerCss ++ styles })
|
||||
|
||||
|
||||
{-| Remove default spacing from the Input.
|
||||
-}
|
||||
noMargin : Bool -> Attribute msg
|
||||
noMargin removeMargin =
|
||||
Attribute (\soFar -> { soFar | noMarginTop = removeMargin })
|
||||
|
||||
|
||||
{-| Use this helper to add custom attributes.
|
||||
|
||||
Do NOT use this helper to add css styles, as they may not be applied the way
|
||||
you want/expect if underlying styles change.
|
||||
Instead, please use the `css` helper.
|
||||
|
||||
-}
|
||||
custom : List (Html.Attribute Never) -> Attribute msg
|
||||
custom attributes =
|
||||
Attribute (\soFar -> { soFar | custom = soFar.custom ++ attributes })
|
||||
|
||||
|
||||
{-| Set a custom ID for this text area. If you don't set the id explicitly,
|
||||
we'll automatically generate one from the label you pass in, but this can
|
||||
cause problems if you have more than one textarea with the same label on
|
||||
the page. Use this to be more specific and avoid issues with duplicate IDs.
|
||||
-}
|
||||
id : String -> Attribute msg
|
||||
id id_ =
|
||||
Attribute (\soFar -> { soFar | id = Just id_ })
|
||||
|
||||
|
||||
{-| -}
|
||||
nriDescription : String -> Attribute msg
|
||||
nriDescription description =
|
||||
custom [ Extra.nriDescription description ]
|
||||
|
||||
|
||||
{-| -}
|
||||
testId : String -> Attribute msg
|
||||
testId id_ =
|
||||
custom [ Extra.testId id_ ]
|
||||
|
||||
|
||||
{-| Use the Standard theme for the TextArea. This is the default.
|
||||
-}
|
||||
standard : Attribute msg
|
||||
standard =
|
||||
Attribute (\soFar -> { soFar | theme = InputStyles.Standard })
|
||||
|
||||
|
||||
{-| Use the Writing theme for the TextArea.
|
||||
-}
|
||||
writing : Attribute msg
|
||||
writing =
|
||||
Attribute (\soFar -> { soFar | theme = InputStyles.Writing })
|
||||
|
||||
|
||||
{-| -}
|
||||
view : String -> List (Attribute msg) -> Html msg
|
||||
view label attributes =
|
||||
view_ label (applyConfig attributes)
|
||||
|
||||
|
||||
{-| -}
|
||||
view_ : String -> Config msg -> Html msg
|
||||
view_ label config =
|
||||
let
|
||||
autoresizeAttrs =
|
||||
case config.height of
|
||||
AutoResize _ ->
|
||||
[ Attributes.attribute "data-autoresize" "" ]
|
||||
|
||||
Fixed ->
|
||||
[]
|
||||
|
||||
heightForStyle =
|
||||
case config.theme of
|
||||
Standard ->
|
||||
InputStyles.textAreaHeight
|
||||
|
||||
UserGenerated ->
|
||||
InputStyles.textAreaHeight
|
||||
|
||||
Writing ->
|
||||
InputStyles.writingMinHeight
|
||||
|
||||
idValue : String
|
||||
idValue =
|
||||
Maybe.withDefault (generateId label) config.id
|
||||
|
||||
isInError =
|
||||
InputErrorAndGuidanceInternal.getIsInError config.error
|
||||
in
|
||||
Html.styled (Html.node "nri-textarea-v5")
|
||||
[ Css.display Css.block, Css.position Css.relative, Css.batch config.containerCss ]
|
||||
autoresizeAttrs
|
||||
[ Html.styled Html.textarea
|
||||
[ InputStyles.input config.theme
|
||||
, Css.boxSizing Css.borderBox
|
||||
, case config.height of
|
||||
AutoResize minimumHeight ->
|
||||
Css.minHeight (calculateMinHeight config.theme minimumHeight)
|
||||
|
||||
Fixed ->
|
||||
Css.minHeight heightForStyle
|
||||
, if config.noMarginTop then
|
||||
Css.important (Css.marginTop Css.zero)
|
||||
|
||||
else
|
||||
Css.batch []
|
||||
, Css.batch
|
||||
(if config.disabled then
|
||||
[ Css.boxShadow Css.none |> Css.important
|
||||
, Css.backgroundColor Colors.gray85
|
||||
]
|
||||
|
||||
else
|
||||
[]
|
||||
)
|
||||
]
|
||||
([ Maybe.map Events.onInput config.onInput
|
||||
|> Maybe.withDefault Extra.none
|
||||
, Maybe.map Events.onBlur config.onBlur
|
||||
|> Maybe.withDefault Extra.none
|
||||
, Attributes.value config.value
|
||||
, Attributes.disabled config.disabled
|
||||
, Attributes.id idValue
|
||||
, Attributes.autofocus config.autofocus
|
||||
, Attributes.placeholder (Maybe.withDefault label config.placeholder)
|
||||
, Attributes.attribute "data-gramm" "false" -- disables grammarly to prevent https://github.com/NoRedInk/NoRedInk/issues/14859
|
||||
, Attributes.class "override-sass-styles custom-focus-ring"
|
||||
, Attributes.classList
|
||||
[ ( InputStyles.inputClass, True )
|
||||
, ( InputStyles.errorClass, isInError )
|
||||
]
|
||||
, Aria.invalid isInError
|
||||
, InputErrorAndGuidanceInternal.describedBy idValue config
|
||||
]
|
||||
++ List.map (Attributes.map never) config.custom
|
||||
)
|
||||
[]
|
||||
, InputLabelInternal.view
|
||||
{ for = idValue
|
||||
, label = label
|
||||
, theme = config.theme
|
||||
}
|
||||
config
|
||||
, InputErrorAndGuidanceInternal.view idValue config
|
||||
]
|
||||
|
||||
|
||||
calculateMinHeight : Theme -> Height -> Css.Px
|
||||
calculateMinHeight textAreaStyle specifiedHeight =
|
||||
{- On including padding in this calculation:
|
||||
|
||||
When the textarea is autoresized, TextArea.js updates the textarea's
|
||||
height by taking its scrollHeight. Because scrollHeight's calculation
|
||||
includes the element's padding no matter what [1], we need to set the
|
||||
textarea's box-sizing to border-box in order to use the same measurement
|
||||
for its height as scrollHeight.
|
||||
|
||||
So, min-height also needs to be specified in terms of padding + content
|
||||
height.
|
||||
|
||||
[1] https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight
|
||||
-}
|
||||
case specifiedHeight of
|
||||
SingleLine ->
|
||||
case textAreaStyle of
|
||||
Standard ->
|
||||
singleLineHeight
|
||||
|
||||
UserGenerated ->
|
||||
singleLineHeight
|
||||
|
||||
Writing ->
|
||||
writingSingleLineHeight
|
||||
|
||||
DefaultHeight ->
|
||||
case textAreaStyle of
|
||||
Standard ->
|
||||
InputStyles.textAreaHeight
|
||||
|
||||
UserGenerated ->
|
||||
InputStyles.textAreaHeight
|
||||
|
||||
Writing ->
|
||||
InputStyles.writingMinHeight
|
||||
|
||||
|
||||
singleLineHeight : Css.Px
|
||||
singleLineHeight =
|
||||
px (.numericValue InputStyles.inputPaddingVertical + .numericValue InputStyles.inputLineHeight + .numericValue InputStyles.inputPaddingVertical)
|
||||
|
||||
|
||||
writingSingleLineHeight : Css.Px
|
||||
writingSingleLineHeight =
|
||||
px (.numericValue InputStyles.writingPaddingTop + .numericValue InputStyles.writingLineHeight + .numericValue InputStyles.writingPadding)
|
||||
|
||||
|
||||
{-| -}
|
||||
generateId : String -> String
|
||||
generateId labelText =
|
||||
"nri-ui-text-area-" ++ (dashify <| removePunctuation labelText)
|
@ -63,7 +63,7 @@ import Nri.Ui.ClickableSvg.V2 as ClickableSvg
|
||||
import Nri.Ui.ClickableText.V3 as ClickableText
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Html.Attributes.V2 as Extra
|
||||
import Nri.Ui.InputStyles.V3 as InputStyles exposing (defaultMarginTop)
|
||||
import Nri.Ui.InputStyles.V4 as InputStyles exposing (defaultMarginTop)
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
import Nri.Ui.Util exposing (dashify)
|
||||
@ -465,7 +465,7 @@ value value_ =
|
||||
Attribute { emptyEventsAndValues | currentValue = Just value_ } identity
|
||||
|
||||
|
||||
{-| If not explicit placeholder is given, the input label will be used as the placeholder.
|
||||
{-| If no explicit placeholder is given, the input label will be used as the placeholder.
|
||||
-}
|
||||
placeholder : String -> Attribute value msg
|
||||
placeholder text_ =
|
||||
|
@ -8,6 +8,7 @@ module CommonControls exposing
|
||||
, content
|
||||
, httpError
|
||||
, romeoAndJulietQuotation
|
||||
, guidanceAndErrorMessage
|
||||
, disabledListItem, premiumDisplay
|
||||
)
|
||||
|
||||
@ -26,9 +27,11 @@ module CommonControls exposing
|
||||
@docs content
|
||||
@docs httpError
|
||||
@docs romeoAndJulietQuotation
|
||||
@docs guidanceAndErrorMessage
|
||||
|
||||
-}
|
||||
|
||||
import Code
|
||||
import Css
|
||||
import Debug.Control as Control exposing (Control)
|
||||
import Debug.Control.Extra as ControlExtra
|
||||
@ -394,3 +397,32 @@ css_ helperName ( styles, default ) { moduleName, use } =
|
||||
, use default
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
guidanceAndErrorMessage :
|
||||
{ moduleName : String
|
||||
, guidance : String -> b
|
||||
, errorMessage : Maybe String -> b
|
||||
, message : String
|
||||
}
|
||||
-> Control (List ( String, b ))
|
||||
-> Control (List ( String, b ))
|
||||
guidanceAndErrorMessage { moduleName, guidance, errorMessage, message } =
|
||||
ControlExtra.optionalListItem "guidance"
|
||||
(Control.string message
|
||||
|> Control.map
|
||||
(\str ->
|
||||
( Code.fromModule moduleName "guidance " ++ Code.string str
|
||||
, guidance str
|
||||
)
|
||||
)
|
||||
)
|
||||
>> ControlExtra.optionalListItem "errorMessage"
|
||||
(Control.map
|
||||
(\str ->
|
||||
( Code.fromModule moduleName "errorMessage " ++ Code.withParens (Code.maybeString (Just str))
|
||||
, errorMessage (Just str)
|
||||
)
|
||||
)
|
||||
(Control.string message)
|
||||
)
|
||||
|
@ -294,26 +294,12 @@ controlAttributes =
|
||||
]
|
||||
)
|
||||
|> ControlExtra.optionalListItem "disclosure" controlDisclosure
|
||||
|> ControlExtra.optionalListItem "errorMessage"
|
||||
(Control.map
|
||||
(\message ->
|
||||
( "RadioButton.errorMessage (Just \"" ++ message ++ "\")"
|
||||
, RadioButton.errorMessage (Just message)
|
||||
)
|
||||
)
|
||||
<|
|
||||
Control.string "The statement must be true."
|
||||
)
|
||||
|> ControlExtra.optionalListItem "guidance"
|
||||
(Control.map
|
||||
(\content ->
|
||||
( "RadioButton.guidance \"" ++ content ++ "\""
|
||||
, RadioButton.guidance content
|
||||
)
|
||||
)
|
||||
<|
|
||||
Control.string "The statement must be true."
|
||||
)
|
||||
|> CommonControls.guidanceAndErrorMessage
|
||||
{ moduleName = moduleName
|
||||
, guidance = RadioButton.guidance
|
||||
, errorMessage = RadioButton.errorMessage
|
||||
, message = "The statement must be true."
|
||||
}
|
||||
|
||||
|
||||
labelVisibility : Control ( String, RadioButton.Attribute Selection Msg )
|
||||
|
@ -9,6 +9,7 @@ module Examples.Select exposing (Msg, State, example)
|
||||
import Accessibility.Styled.Key as Key
|
||||
import Category exposing (Category(..))
|
||||
import Code
|
||||
import CommonControls
|
||||
import Css
|
||||
import Debug.Control as Control exposing (Control)
|
||||
import Debug.Control.Extra as ControlExtra
|
||||
@ -153,25 +154,12 @@ initControls =
|
||||
)
|
||||
(Control.bool True)
|
||||
)
|
||||
|> ControlExtra.optionalListItem "errorMessage"
|
||||
(Control.map
|
||||
(\str ->
|
||||
( "Select.errorMessage (Just \"" ++ str ++ "\")"
|
||||
, Select.errorMessage (Just str)
|
||||
)
|
||||
)
|
||||
(Control.string "The right item must be selected.")
|
||||
)
|
||||
|> ControlExtra.optionalListItem "guidance"
|
||||
(Control.map
|
||||
(\str ->
|
||||
( "Select.guidance \"" ++ str ++ "\""
|
||||
, Select.guidance str
|
||||
)
|
||||
)
|
||||
<|
|
||||
Control.string "The right item must be selected."
|
||||
)
|
||||
|> CommonControls.guidanceAndErrorMessage
|
||||
{ moduleName = moduleName
|
||||
, guidance = Select.guidance
|
||||
, errorMessage = Select.errorMessage
|
||||
, message = "The right item must be selected."
|
||||
}
|
||||
|> ControlExtra.optionalListItem "disabled"
|
||||
(Control.value ( "Select.disabled", Select.disabled ))
|
||||
|> ControlExtra.optionalListItem "loading"
|
||||
|
@ -8,16 +8,18 @@ module Examples.TextArea exposing (Msg, State, example)
|
||||
|
||||
import Category exposing (Category(..))
|
||||
import Code
|
||||
import CommonControls
|
||||
import Css
|
||||
import Debug.Control as Control exposing (Control)
|
||||
import Debug.Control.Extra as ControlExtra
|
||||
import Debug.Control.View as ControlView
|
||||
import Example exposing (Example)
|
||||
import Html.Styled as Html exposing (Html)
|
||||
import Html.Styled as Html
|
||||
import Html.Styled.Attributes as Attributes exposing (css)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Heading.V3 as Heading
|
||||
import Nri.Ui.InputStyles.V3 as InputStyles exposing (Theme(..))
|
||||
import Nri.Ui.TextArea.V4 as TextArea
|
||||
import Nri.Ui.InputStyles.V4 as InputStyles exposing (Theme(..))
|
||||
import Nri.Ui.TextArea.V5 as TextArea
|
||||
|
||||
|
||||
moduleName : String
|
||||
@ -27,7 +29,7 @@ moduleName =
|
||||
|
||||
version : Int
|
||||
version =
|
||||
4
|
||||
5
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -66,29 +68,15 @@ example =
|
||||
, view =
|
||||
\ellieLinkConfig state ->
|
||||
let
|
||||
settings =
|
||||
{ label, attributes } =
|
||||
Control.currentValue state.settings
|
||||
|
||||
toExampleCode name =
|
||||
[ moduleName ++ "." ++ name
|
||||
, Code.record
|
||||
[ ( "value", Code.string state.value )
|
||||
, ( "autofocus", Code.bool False )
|
||||
, ( "onInput", "identity" )
|
||||
, ( "onBlur"
|
||||
, Code.maybe <|
|
||||
if settings.onBlur then
|
||||
Just (Code.string "Neener neener Blur happened")
|
||||
|
||||
else
|
||||
Nothing
|
||||
)
|
||||
, ( "isInError", Code.bool settings.isInError )
|
||||
, ( "label", Code.string settings.label )
|
||||
, ( "height", Tuple.first settings.height )
|
||||
, ( "placeholder", Code.string settings.placeholder )
|
||||
, ( "showLabel", Code.bool settings.showLabel )
|
||||
]
|
||||
[ moduleName ++ "." ++ name ++ " " ++ Code.string label
|
||||
, Code.list <|
|
||||
("TextArea.value " ++ Code.string state.value)
|
||||
:: "TextArea.onInput identity"
|
||||
:: List.map Tuple.first attributes
|
||||
]
|
||||
|> String.join ""
|
||||
in
|
||||
@ -106,28 +94,14 @@ example =
|
||||
[ { sectionName = "view"
|
||||
, code = toExampleCode "view"
|
||||
}
|
||||
, { sectionName = "writing"
|
||||
, code = toExampleCode "writing"
|
||||
}
|
||||
]
|
||||
}
|
||||
, Heading.h2 [ Heading.plaintext "Example" ]
|
||||
, settings.theme
|
||||
{ value = state.value
|
||||
, autofocus = False
|
||||
, onInput = UpdateValue
|
||||
, onBlur =
|
||||
if settings.onBlur then
|
||||
Just (UpdateValue "Neener neener Blur happened")
|
||||
|
||||
else
|
||||
Nothing
|
||||
, isInError = settings.isInError
|
||||
, label = settings.label
|
||||
, height = Tuple.second settings.height
|
||||
, placeholder = settings.placeholder
|
||||
, showLabel = settings.showLabel
|
||||
}
|
||||
, TextArea.view label
|
||||
(TextArea.value state.value
|
||||
:: TextArea.onInput UpdateValue
|
||||
:: List.map Tuple.second attributes
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@ -148,49 +122,61 @@ init =
|
||||
|
||||
|
||||
type alias Settings =
|
||||
{ theme : TextArea.Model Msg -> Html Msg
|
||||
, label : String
|
||||
, showLabel : Bool
|
||||
, placeholder : String
|
||||
, isInError : Bool
|
||||
, onBlur : Bool
|
||||
, height : ( String, TextArea.HeightBehavior )
|
||||
{ label : String
|
||||
, attributes : List ( String, TextArea.Attribute Msg )
|
||||
}
|
||||
|
||||
|
||||
controlAttributes : Control (List ( String, TextArea.Attribute Msg ))
|
||||
controlAttributes =
|
||||
ControlExtra.list
|
||||
|> ControlExtra.optionalListItem
|
||||
"theme"
|
||||
(CommonControls.choice moduleName
|
||||
[ ( "standard", TextArea.standard )
|
||||
, ( "writing", TextArea.writing )
|
||||
]
|
||||
)
|
||||
|> ControlExtra.optionalBoolListItem "onBlur"
|
||||
( "TextArea.onBlur " ++ Code.string "Neener neener Blur happened"
|
||||
, TextArea.onBlur (UpdateValue "Neener neener Blur happened")
|
||||
)
|
||||
|> ControlExtra.optionalListItem "placeholder"
|
||||
(Control.string "A long time ago, in a galaxy pretty near here actually..."
|
||||
|> Control.map
|
||||
(\str ->
|
||||
( "TextArea.placeholder " ++ Code.string str
|
||||
, TextArea.placeholder str
|
||||
)
|
||||
)
|
||||
)
|
||||
|> ControlExtra.optionalListItem "height"
|
||||
(CommonControls.choice moduleName
|
||||
[ ( "autoResize", TextArea.autoResize )
|
||||
, ( "autoResizeSingleLine", TextArea.autoResizeSingleLine )
|
||||
]
|
||||
)
|
||||
|> CommonControls.guidanceAndErrorMessage
|
||||
{ moduleName = moduleName
|
||||
, guidance = TextArea.guidance
|
||||
, errorMessage = TextArea.errorMessage
|
||||
, message = "The statement must be true."
|
||||
}
|
||||
|> ControlExtra.optionalBoolListItem "disabled"
|
||||
( "TextArea.disabled", TextArea.disabled )
|
||||
|> ControlExtra.optionalBoolListItem "noMargin"
|
||||
( "TextArea.noMargin True", TextArea.noMargin True )
|
||||
|> ControlExtra.optionalBoolListItem "css"
|
||||
( "TextArea.css [ Css.backgroundColor Colors.azure ]"
|
||||
, TextArea.css [ Css.backgroundColor Colors.azure ]
|
||||
)
|
||||
|
||||
|
||||
initControls : Control Settings
|
||||
initControls =
|
||||
Control.record Settings
|
||||
|> Control.field "theme"
|
||||
(Control.choice
|
||||
[ ( "view", Control.value TextArea.view )
|
||||
, ( "writing", Control.value TextArea.writing )
|
||||
]
|
||||
)
|
||||
|> Control.field "label" (Control.string "Introductory paragraph")
|
||||
|> Control.field "showLabel" (Control.bool True)
|
||||
|> Control.field "placeholder" (Control.string "A long time ago, in a galaxy pretty near here actually...")
|
||||
|> Control.field "isInError" (Control.bool False)
|
||||
|> Control.field "onBlur" (Control.bool False)
|
||||
|> Control.field "height"
|
||||
(Control.choice
|
||||
[ ( "fixed"
|
||||
, Control.value ( "TextArea.Fixed", TextArea.Fixed )
|
||||
)
|
||||
, ( "autoresize default"
|
||||
, Control.value
|
||||
( Code.withParens "TextArea.AutoResize TextArea.DefaultHeight"
|
||||
, TextArea.AutoResize TextArea.DefaultHeight
|
||||
)
|
||||
)
|
||||
, ( "autoresize singleline"
|
||||
, Control.value
|
||||
( Code.withParens "TextArea.AutoResize TextArea.SingleLine"
|
||||
, TextArea.AutoResize TextArea.SingleLine
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Control.field "attributes" controlAttributes
|
||||
|
||||
|
||||
{-| -}
|
||||
|
@ -10,6 +10,7 @@ import Accessibility.Styled exposing (..)
|
||||
import Accessibility.Styled.Key as Key
|
||||
import Category exposing (Category(..))
|
||||
import Code
|
||||
import CommonControls
|
||||
import Css
|
||||
import Debug.Control as Control exposing (Control)
|
||||
import Debug.Control.Extra as ControlExtra
|
||||
@ -385,24 +386,12 @@ controlAttributes =
|
||||
)
|
||||
|> ControlExtra.optionalBoolListItem "hiddenLabel"
|
||||
( "TextInput.hiddenLabel", TextInput.hiddenLabel )
|
||||
|> ControlExtra.optionalListItem "errorMessage"
|
||||
(Control.map
|
||||
(\str ->
|
||||
( "TextInput.errorMessage " ++ Code.withParens (Code.maybeString (Just str))
|
||||
, TextInput.errorMessage (Just str)
|
||||
)
|
||||
)
|
||||
(Control.string "The statement must be true.")
|
||||
)
|
||||
|> ControlExtra.optionalListItem "guidance"
|
||||
(Control.string "The statement must be true."
|
||||
|> Control.map
|
||||
(\str ->
|
||||
( "TextInput.guidance " ++ Code.string str
|
||||
, TextInput.guidance str
|
||||
)
|
||||
)
|
||||
)
|
||||
|> CommonControls.guidanceAndErrorMessage
|
||||
{ moduleName = moduleName
|
||||
, guidance = TextInput.guidance
|
||||
, errorMessage = TextInput.errorMessage
|
||||
, message = "The statement must be true."
|
||||
}
|
||||
|> ControlExtra.optionalBoolListItem "disabled"
|
||||
( "TextInput.disabled", TextInput.disabled )
|
||||
|> ControlExtra.optionalBoolListItem "loading"
|
||||
|
@ -36,6 +36,7 @@
|
||||
"Nri.Ui.Html.Attributes.V2",
|
||||
"Nri.Ui.Html.V3",
|
||||
"Nri.Ui.InputStyles.V3",
|
||||
"Nri.Ui.InputStyles.V4",
|
||||
"Nri.Ui.Loading.V1",
|
||||
"Nri.Ui.Logo.V1",
|
||||
"Nri.Ui.MasteryIcon.V1",
|
||||
@ -45,6 +46,7 @@
|
||||
"Nri.Ui.Modal.V11",
|
||||
"Nri.Ui.Page.V3",
|
||||
"Nri.Ui.Palette.V1",
|
||||
"Nri.Ui.Panel.V1",
|
||||
"Nri.Ui.Pennant.V2",
|
||||
"Nri.Ui.PremiumCheckbox.V8",
|
||||
"Nri.Ui.RadioButton.V4",
|
||||
@ -64,6 +66,7 @@
|
||||
"Nri.Ui.Text.V6",
|
||||
"Nri.Ui.Text.Writing.V1",
|
||||
"Nri.Ui.TextArea.V4",
|
||||
"Nri.Ui.TextArea.V5",
|
||||
"Nri.Ui.TextInput.V7",
|
||||
"Nri.Ui.Tooltip.V3",
|
||||
"Nri.Ui.UiIcon.V1"
|
||||
|
Loading…
Reference in New Issue
Block a user