Merge pull request #63 from NoRedInk/textarea-v2-minimum-height

Textarea v2 adding a minimum height
This commit is contained in:
Marica Odagaki 2018-05-30 17:14:47 -07:00 committed by GitHub
commit 1acc5996e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 328 additions and 15 deletions

View File

@ -41,6 +41,7 @@
"Nri.Ui.Text.V1",
"Nri.Ui.Text.V2",
"Nri.Ui.TextArea.V1",
"Nri.Ui.TextArea.V2",
"Nri.Ui.TextInput.V1",
"Nri.Ui.TextInput.V2",
"Nri.Ui"
@ -55,6 +56,7 @@
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0",
"rtfeldman/elm-css-util": "1.0.2 <= v < 2.0.0",
"tesk9/accessible-html": "3.0.0 <= v < 4.0.0",
"tesk9/accessible-html-with-css": "1.0.1 <= v < 2.0.0",
"wernerdegroot/listzipper": "3.0.0 <= v < 4.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"

View File

@ -1,9 +1,14 @@
module Nri.Ui.InputStyles exposing (Assets, CssClasses(..), styles)
module Nri.Ui.InputStyles exposing (Assets, CssClasses(..), inputLineHeight, inputPaddingVertical, styles, textAreaHeight, writingLineHeight, writingMinHeight, writingPadding, writingPaddingTop)
{-|
@docs styles, CssClasses
## Shared hardcoded values
@docs inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight
-}
import Css exposing (..)
@ -60,7 +65,7 @@ styles =
inputStyle =
[ border3 (px 1) solid gray75
, borderRadius (px 8)
, padding2 (px 8) (px 14)
, padding2 inputPaddingVertical (px 14)
, property "transition" "all 0.1s ease"
, pseudoClass "placeholder"
[ color gray45
@ -73,7 +78,7 @@ styles =
, display inlineBlock
, verticalAlign top
, marginBottom zero
, lineHeight (px 20)
, lineHeight inputLineHeight
, marginTop (px 9)
, boxShadow6 inset zero (px 2) zero zero gray92
, property "transition" "all 0.4s ease"
@ -98,7 +103,7 @@ styles =
, selector "textarea"
[ withClass Input
(inputStyle
++ [ height (px 100)
++ [ height textAreaHeight
, width (pct 100)
]
)
@ -123,9 +128,9 @@ styles =
[ class Input
[ Nri.Ui.Fonts.V1.quizFont
, fontSize (px 20)
, lineHeight (px 25)
, padding (px 15)
, paddingTop (px 20)
, lineHeight writingLineHeight
, padding writingPadding
, paddingTop writingPaddingTop
, height auto
]
, class Label
@ -141,7 +146,7 @@ styles =
, borderColor azure
]
, selector "textarea"
[ minHeight (px 150)
[ minHeight writingMinHeight
]
, selector "input"
[ textAlign center
@ -253,3 +258,38 @@ type alias Assets r =
, icons_searchGray_svg : Asset
, icons_xBlue_svg : Asset
}
inputPaddingVertical : Px
inputPaddingVertical =
px 8
inputLineHeight : Px
inputLineHeight =
px 20
textAreaHeight : Px
textAreaHeight =
px 100
writingLineHeight : Px
writingLineHeight =
px 25
writingPadding : Px
writingPadding =
px 15
writingPaddingTop : Px
writingPaddingTop =
px 20
writingMinHeight : Px
writingMinHeight =
px 150

244
src/Nri/Ui/TextArea/V2.elm Normal file
View File

@ -0,0 +1,244 @@
module Nri.Ui.TextArea.V2
exposing
( Height(..)
, HeightBehavior(..)
, Model
, contentCreation
, generateId
, styles
, view
, writing
)
{-|
## Upgrading from V2
- The Model's autoResize field is now `height : HeightBehavior`, which can be
either `Fixed` or `AutoResize Height`. `Height` is either
`DefaultHeight` or `SingleLine` and controls the minimum height of the textarea.
- The view now returns `Html.Styled` rather than plain `Html`.
## The Nri styleguide-specified textarea with overlapping label
@docs view, writing, contentCreation, Height, HeightBehavior, Model, generateId, styles
-}
import Accessibility.Styled.Style
import Css exposing ((|+|))
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes
import Html.Styled.Events as Events
import Nri.Ui.InputStyles as InputStyles exposing (CssClasses(..))
import Nri.Ui.Styles.V1
import Nri.Ui.Util exposing (dashify, removePunctuation)
{-| -}
type alias Model msg =
{ value : String
, autofocus : Bool
, onInput : String -> msg
, isInError : Bool
, height : HeightBehavior
, placeholder : String
, label : String
, showLabel : Bool
}
{-| Control whether to auto-expand the height.
-}
type HeightBehavior
= Fixed
| AutoResize Height
{-| For specifying the actual height.
-}
type Height
= DefaultHeight
| SingleLine
{-| -}
view : Model msg -> Html msg
view model =
view_ DefaultStyle model
{-| Used for Writing Cycles
-}
writing : Model msg -> Html msg
writing model =
view_ WritingStyle model
{-| Used for Content Creation
-}
contentCreation : Model msg -> Html msg
contentCreation model =
view_ ContentCreationStyle model
type TextAreaStyle
= DefaultStyle
| WritingStyle
| ContentCreationStyle
{-| -}
view_ : TextAreaStyle -> Model msg -> Html msg
view_ textAreaStyle model =
let
showWritingClass =
textAreaStyle == WritingStyle
showContentCreationClass =
textAreaStyle == ContentCreationStyle
minHeight =
case model.height of
Fixed ->
[]
AutoResize minimumHeight ->
-- FIXME: Css.important is needed here because InputStyles's
-- min-height rule has more specificity. It can go away once
-- we've fully migrated to Html.Styled.
[ calculateMinHeight textAreaStyle minimumHeight
|> Css.minHeight
|> Css.important
]
sharedAttributes =
[ Events.onInput model.onInput
, Attributes.id (generateId model.label)
, styles.class [ Input ]
|> Attributes.fromUnstyled
, Attributes.autofocus model.autofocus
, Attributes.placeholder model.placeholder
, Attributes.attribute "data-gramm" "false" -- disables grammarly to prevent https://github.com/NoRedInk/NoRedInk/issues/14859
, Attributes.css
(minHeight
++ [ Css.boxSizing Css.borderBox ]
)
]
in
Html.div
[ styles.classList
[ ( Container, True )
, ( IsInError, model.isInError )
, ( Writing, showWritingClass )
, ( ContentCreation, showContentCreationClass )
]
|> Attributes.fromUnstyled
]
[ case model.height of
AutoResize _ ->
{- NOTES:
The autoresize-textarea element is implemented to pass information applied to itself to an internal
textarea element that it inserts into the DOM automatically. Maintaing this behavior may require some
changes on your part, as listed below.
- When adding an Html.Attribute that is a _property_, you must edit Nri/TextArea.js to ensure that a getter and setter
are set up to properly reflect the property to the actual textarea element that autoresize-textarea creates
- When adding a new listener from Html.Events, you must edit Nri/TextArea.js to ensure that a listener is set up on
the textarea that will trigger this event on the autoresize-textarea element itself. See AutoresizeTextArea.prototype._onInput
and AutoresizeTextArea.prototype.connectedCallback for an example pertaining to the `input` event
- When adding a new Html.Attribute that is an _attribute_, you don't have to do anything. All attributes are
automatically reflected onto the textarea element via AutoresizeTextArea.prototype.attributeChangedCallback
-}
Html.node "autoresize-textarea"
(sharedAttributes
++ [ -- setting the default value via a text node doesn't play well with the custom element,
-- but we'll be able to switch to the regular value property in 0.19 anyway
Attributes.defaultValue model.value
]
)
[]
Fixed ->
Html.textarea sharedAttributes
[ Html.text model.value ]
, if not model.showLabel then
Html.label
[ Attributes.for (generateId model.label)
, styles.class [ Label ]
|> Attributes.fromUnstyled
, Accessibility.Styled.Style.invisible
]
[ Html.text model.label ]
else
Html.label
[ Attributes.for (generateId model.label)
, styles.class [ Label ]
|> Attributes.fromUnstyled
]
[ Html.text model.label ]
]
calculateMinHeight : TextAreaStyle -> 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
DefaultStyle ->
singleLineHeight
WritingStyle ->
writingSingleLineHeight
ContentCreationStyle ->
singleLineHeight
DefaultHeight ->
case textAreaStyle of
DefaultStyle ->
InputStyles.textAreaHeight
WritingStyle ->
InputStyles.writingMinHeight
ContentCreationStyle ->
InputStyles.textAreaHeight
singleLineHeight : Css.Px
singleLineHeight =
InputStyles.inputPaddingVertical |+| InputStyles.inputLineHeight |+| InputStyles.inputPaddingVertical
writingSingleLineHeight : Css.Px
writingSingleLineHeight =
InputStyles.writingPaddingTop |+| InputStyles.writingLineHeight |+| InputStyles.writingPadding
{-| -}
generateId : String -> String
generateId labelText =
"nri-ui-text-area-" ++ (dashify <| removePunctuation labelText)
{-| -}
styles : Nri.Ui.Styles.V1.StylesWithAssets Never CssClasses msg (InputStyles.Assets r)
styles =
InputStyles.styles

View File

@ -6,13 +6,14 @@ module Examples.TextArea exposing (Msg, State, example, init, update)
-}
import Css
import Dict exposing (Dict)
import Html
import Html.Styled
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
import Nri.Ui.Checkbox.V2 as Checkbox
import Nri.Ui.Text.V2 as Text
import Nri.Ui.TextArea.V1 as TextArea
import Nri.Ui.TextArea.V2 as TextArea
{-| -}
@ -76,30 +77,45 @@ example parentMessage state =
, onInput = InputGiven 1
, isInError = state.isInError
, label = "TextArea.view"
, autoResize = state.autoResize
, height =
if state.autoResize then
TextArea.AutoResize TextArea.SingleLine
else
TextArea.Fixed
, placeholder = "Placeholder"
, showLabel = state.showLabel
}
|> Html.Styled.toUnstyled
, TextArea.writing
{ value = Maybe.withDefault "" <| Dict.get 2 state.textValues
, autofocus = False
, onInput = InputGiven 2
, isInError = state.isInError
, label = "TextArea.writing"
, autoResize = state.autoResize
, height =
if state.autoResize then
TextArea.AutoResize TextArea.DefaultHeight
else
TextArea.Fixed
, placeholder = "Placeholder"
, showLabel = state.showLabel
}
|> Html.Styled.toUnstyled
, TextArea.contentCreation
{ value = Maybe.withDefault "" <| Dict.get 3 state.textValues
, autofocus = False
, onInput = InputGiven 3
, isInError = state.isInError
, label = "TextArea.contentCreation"
, autoResize = state.autoResize
, height =
if state.autoResize then
TextArea.AutoResize TextArea.DefaultHeight
else
TextArea.Fixed
, placeholder = "Placeholder"
, showLabel = state.showLabel
}
|> Html.Styled.toUnstyled
]
|> List.map (Html.map parentMessage)
}

View File

@ -24,7 +24,8 @@ import Nri.Ui.Dropdown.V1
import Nri.Ui.Icon.V2
import Nri.Ui.SegmentedControl.V5
import Nri.Ui.Select.V2
import Nri.Ui.TextArea.V1 as TextArea
import Nri.Ui.Text.V2 as Text
import Nri.Ui.TextArea.V2 as TextArea
import String.Extra

View File

@ -43,23 +43,30 @@ Object.defineProperties(AutoresizeTextArea.prototype, {
}
});
AutoresizeTextArea.prototype._onInput = function() {
AutoresizeTextArea.prototype._resize = function() {
var minHeight = null;
if (this._textarea.style.minHeight) {
minHeight = parseInt(this._textarea.style.minHeight, 10);
} else {
minHeight = parseInt(window.getComputedStyle(this._textarea).minHeight, 10);
}
if (minHeight === 0) {
minHeight = parseInt(window.getComputedStyle(this._textarea).height, 10);
}
this._textarea.style.overflowY = "hidden";
this._textarea.style.minHeight = minHeight + "px";
this._textarea.style.transition = "none";
if (this._textarea.scrollHeight > minHeight) {
this._textarea.style.height = "auto";
this._textarea.style.height = minHeight + "px";
this._textarea.style.height = this._textarea.scrollHeight + "px";
} else {
this._textarea.style.height = minHeight + "px";
}
};
AutoresizeTextArea.prototype._onInput = function() {
this._resize();
this.dispatchEvent(new Event("input"));
};
@ -91,6 +98,7 @@ AutoresizeTextArea.prototype.connectedCallback = function() {
this._textarea.addEventListener("input", this._onInput);
this._reflectAttributes();
this.appendChild(this._textarea);
this._resize();
};
AutoresizeTextArea.prototype.disconnectedCallback = function() {

View File

@ -21,6 +21,7 @@
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0",
"rtfeldman/elm-css-util": "1.0.2 <= v < 2.0.0",
"tesk9/accessible-html": "3.0.0 <= v < 4.0.0",
"tesk9/accessible-html-with-css": "1.0.1 <= v < 2.0.0",
"wernerdegroot/listzipper": "3.0.0 <= v < 4.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"

View File

@ -19,6 +19,7 @@
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0",
"rtfeldman/elm-css-util": "1.0.2 <= v < 2.0.0",
"tesk9/accessible-html": "3.0.0 <= v < 4.0.0",
"tesk9/accessible-html-with-css": "1.0.1 <= v < 2.0.0",
"wernerdegroot/listzipper": "3.0.0 <= v < 4.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"