elm에 사용자 정의 요소 작성

요약: 통합 JavaScript와 Elm은 두 가지 선택이 있습니다. 하나는 이미 한동안 존재해 온 포트 시스템이고, 다른 하나는 사용자 정의 요소를 사용하는 것입니다.
본고에서 우리는 그것이 상당히 간단하다는 것을 볼 수 있을 뿐만 아니라, 그것을 사용하는 두 개의 가방 예시를 보여 줄 것이다.
소개는 좀 길지만 main part으로 바로 넘어갈 수 있습니다.

사용자 정의 요소란 무엇입니까?


사용자 정의 요소는 웹 구성 요소의 일부입니다. 간단히 말하면 새 HTML 태그를 만들 수 있습니다. 이 태그는 JavaScript에서 정의한 행동을 가지고 있습니다.
이를 "탭에 매우 작은 응용 프로그램 봉인"으로 간주할 수 있습니다.
너는 작은 것을 <drawing-board tool="pencil" thickness="10pt"></drawing-board>이라고 부르고 그것과 조립된 전체 기능을 얻을 수 있다고 정의할 생각은 없니?
응, 사용자 정의 요소를 사용하면 이렇게 할 수 있어.
당신이 그것을 생각할 때, 입력은 보통 <textarea>과 많은 기능과'상태'를 포함하고 있으며, 사용자의 입력이 무엇인지, 커서가 어디에 있는지, 만약 자동으로 완성되면 사용할 수 있습니다.
사용자 정의 요소는 자신의 버전을 정의하는 간결한 방식일 뿐이다.
사용자 정의 요소에 대한 자세한 내용은 다음 게시물을 참조하십시오.


또는 위대하고 전능한 MDN: Using custom elements 참조

이것은 Elm에게 어떤 도움이 됩니까?


Elm을 모르신다면, Elm는 전단을 위한 함수식 언어입니다.
Haskell의 "경량급"버전과 더 친밀한 버전으로 볼 수 있으며, 이 버전은 단일 작업에 다시 사용됩니다.
많은 장점 중에서 Elm는 컴파일된 코드가 실행 중 오류가 발생하지 않도록 확보합니다.
이를 실현하는 방법 중 하나는 Result 또는 Maybe 같은 구조를 사용하여 사용자가 작성한 코드를 강제로 처리하여 오류가 발생할 수 있는 모든 다른 방식을 처리하는 것입니다. 이러한 구조는 완벽하게 작동할 수 있습니다.
이 모든 것은 매우 긴 소개입니다. 이러한 보증을 제공하기 위해 Elm은 외부의 안전하지 않은 세계와의 상호작용을 제한합니다(일명 JavaScript Doomdom...).
전통적으로 대부분의 상호작용은 ports에서 처리된다.
포트를 통해 외부 세계와elm 사이에서 정보를 교환하는 주요 관심사는elm 코드의 완전성을 확보하는 데 있습니다.
그러나 사용자 정의 요소는elm 코드 라이브러리에 독립된 자바스크립트를 집적하는 재미있는 방식이다.
이것은 예를 들어 도표 라이브러리, 채팅 로봇...
그래, 그래, 좋아, 그럼 어떡하지?그래, 우리 시작합시다.

일을 시키다


elm 문서는 excellent base을 제공하여elm와 상호작용을 시작하는 사용자 정의 요소를 제공합니다.
그러나 shameless plug의 상세한 예시보다 더 좋은 것은 없다.
나는elm의 각 항목에서 내가 한 일은 키보드 이벤트(또는 더 정확히 말하면 키의 조합)를 바탕으로 동작을 촉발하는 것을 자주 발견한다.
과거에 저는 elm/browser package의 사건을 주로 사용했는데 이런 사건들은 운행이 양호하지만 단점도 있습니다(자세한 정보는 this link 참조).
사용자 정의 요소를 만들어서 특정한 단축키를 정탐하면 보기에서 간단함을 유지하고 단축키를 다른 입력으로 볼 수 있습니다.
이 작은 패키지를 사용하면 무시할 수 있는 모드를 만들 수 있습니다.
shortcutModal : List (Html Msg) -> Html Msg
shortcutModal =
    Shortcut.shortcutElement
        [ Shortcut.esc CloseModal ]
        [ class "fixed top-0 bottom-0 left-0 right-0 flex flex-col items-center justify-center bg-gray-500 bg-opacity-75" ]
        << List.singleton
        << div [ class "w-3/4 max-w-4xl p-12 bg-white border-gray-800 rounded-lg shadow-xl" ]
만약 당신이 그 코드에 조금만 접근한다면, 당신은 여기서 두 줄의 관건적인 코드를 볼 수 있을 것이다.
    Shortcut.shortcutElement -- simply a wrapper for Html.node "shortcut-element"
        [ Shortcut.esc CloseModal ] -- the shortcutElement expect a list of shortcut and Shortcut.esc is just a simple way to say "when the user press ESC send me a CloseModal message"
구독과 Browser.Events에 비해 이 버전의 주요 관심사는 가독성에 있다.
이제 UI의 일부분이라도 바로 가기를 사용할 수 있습니다. 구독에서 가시성/상태를 추적할 필요 없이 보기에서 바로 읽을 수 있습니다.
넉넉히코드 보여줘!
전체 코드는 here이지만 이 해결 방안의 주요 구성 부분을 봅시다.

단축키 정의


바로 가기는 보낼 메시지와 조합키 설명의 연결입니다.
키포인트 조합은 기본적인 키포인트와 선택할 수 있는 수정기입니다.
Elm은 이 점을 실현하는 좋은 방법을 제공합니다. 즉, 연합 형식 (TypeScript나 유사한 유형에서 왔다면 매우 강력한 매거 유형으로 보십시오) 과 기록 형식 (마찬가지로, TypeScript 인원은 간단한 종류로 보십시오. 방법이 없고 속성만 있습니다.)
마지막으로 단축키는 다음과 같이 정의됩니다.
type alias Shortcut msg =
    { msg : msg
    , keyCombination :
        { baseKey : Key
        , alt : Maybe Bool
        , shift : Maybe Bool
        , ctrl : Maybe Bool
        , meta : Maybe Bool
        }
    }
Key 유형은 (전체 코드 here)로 정의된 결합 유형입니다.
type Key
    = Escape
    | BackSpace
    -- | ... and many other constructors for the special keys
    | Regular String

사용자 정의 요소 정의


사용자 정의 요소를 실제로 작성하기 전에, 우리가 해야 할 일은polyfill을 설치하는 것입니다.
사용자 정의 요소가 좋은 지원을 받았지만 (Can I use? 참조, 심지어 안드로이드 브라우저도 가입!),IE11을 꾸준히 사용하는 사람들에게polyfill을 사용하고 그들이 빠지지 않도록 확보하는 것은 여전히 안전하고 아름다운 것이다.
정확한 here이 있습니다. NPM을 통해서만 설치할 수 있습니다. 간단하지 않습니까?
완료되면 사용자 정의 요소에 대한 파일을 만들고 다음 브래킷을 놓을 수 있습니다.
import '@webcomponents/custom-elements' // that's our polyfill

// custom elements are really just a custom HTMLElement
// so it is really no surprise that you just need to extends the HTMLElement class
export class ShortcutElement extends HTMLElement { 
  connectedCallback () {
    // here goes the code you want to run when your custom element is rendered and initialised
  }

  disconnectedCallback () {
    // here goes the actions you should do when it's time to destroy/remove your custom element
  }
}

// the last important step here: registering our element so people can actually use it in their HTML
customElements.define('shortcut-element', ShortcutElement)
위의 코드를 보면 HTMLElement의 요소를 백업하고 customElements.define(tagName: string, constructor: HTMLElement)을 통해 태그 이름으로 등록하는 것이 관건입니다.
이제 그것을 가득 채우자.
상기 코드 단락의 주석에서 말한 바와 같이 첫 번째 입구점과 출구점은 두 개의 리셋이다. connectedCallbackdisconnectedCallback이다.
첫 번째는 요소가 페이지에 추가될 때 호출되고, 두 번째는 요소가 삭제될 때 호출됩니다.
바로 가기 예시에서 connectedCallback을 사용하여 body에 이벤트 탐지기를 등록합니다. (이것은 이벤트를 포착하기 때문에 페이지에 무엇이 있든지 상관없습니다.) 그리고 disconnectedCallback을 사용하여 body에서 이벤트 탐지기 구독을 취소합니다.
따라서 다음을 시작합니다.
export class ShortcutElement extends HTMLElement {
  connectedCallback () {
    this.listener = (evt) => {
      const event = evt
      // TODO check with the associated shortcuts if we have a match
      // TODO if we have one then send a custom event
    }
    // let's register
    // NOTE: we will register at the capture phase so as to take precedence over the rest (e.g. textarea, input, ...)
    document.body.addEventListener('keydown', this.listener, { capture: true })
  }

  disconnectedCallback () {
    // let's unregister
    document.body.removeEventListener('keydown', this.listener, {
      capture: true
    })
  }
}
자바스크립트 파트가 곧 완성됩니다!네, 거기에는 두 개의 큰 TODO이 있지만, 우리는elm의 한 면을 보고 돌아올 것입니다

Elm에서는 어떻게 사용합니까?


엘엠 방면에서 일은 상당히 간단하다.우리는 단지 두 가지만 필요로 한다. 우리 요소를 사용하는 사용자 정의 Html.Html msg을 정의하고 이 요소와 통신하는 방법을 찾아야 한다.
첫 번째 부분은 매우 간단하다. Html.node "shortcut-element".
더 나은 기능을 위해 함수에 포장할 수 있습니다.
shortcutElement: List (Html.Attribute msg) -> List (Html msg) -> Html msg
shortcutElement =
  Html.node "shortcut-element"
지금, 통신 부분.이것은 실제로 두 개의 하위 부분이 있는데 그것이 바로 사용자 정의 요소에 대한 정보와 사용자 정의 요소에 대한 정보이다.
JavaScript에서 Elm로 메시지를 보내기 위해 JavaScript 부분에서 CustomEvent 을 사용합니다. 이것은 일반적인 Html.Events.on 함수와 익숙한 Json.Decode (및 Json.Decode.Extra )만 사용할 수 있음을 의미합니다.
Elm World에서 JavaScript로 메시지를 보내기 위해 속성과 속성을 사용합니다.
보아하니 이렇다.
encodeShortcut : Shortcut msg -> Json.Encode.Value
encodeShortcut ({ keyCombination } as shortcut) =
    Json.Encode.object
        [ ( "name", Json.Encode.string <| hashShortcut shortcut )
        , ( "baseKey", Json.Encode.string <| keyToString keyCombination.baseKey )
        , ( "alt", Json.Encode.Extra.maybe Json.Encode.bool keyCombination.alt )
        , ( "shift", Json.Encode.Extra.maybe Json.Encode.bool keyCombination.shift )
        , ( "ctrl", Json.Encode.Extra.maybe Json.Encode.bool keyCombination.ctrl )
        , ( "meta", Json.Encode.Extra.maybe Json.Encode.bool keyCombination.meta )
        ]


onShortcut : List (Shortcut msg) -> Html.Attribute msg
onShortcut shortcuts =
    Html.Events.on "shortcut"
        (Json.Decode.at [ "detail", "name" ] Json.Decode.string
            |> Json.Decode.andThen
                (\hash ->
                    List.Extra.find (hashShortcut >> (==) hash) shortcuts
                        -- NOTE: if a event decoding failed then no message is emitted
                        |> Maybe.Extra.unwrap (Json.Decode.fail "did not match a known shortcut") (.msg >> Json.Decode.succeed)
                )
        )


shortcutElement : List (Shortcut msg) -> List (Html.Attribute msg) -> List (Html msg) -> Html msg
shortcutElement shortcuts attrs =
    node "shortcut-element"
        -- Add 2 attributes here: one to send the props we're listening to
        (Html.Attributes.property "shortcuts" (Json.Encode.list encodeShortcut shortcuts)
            -- one to listen to the stuff
            :: onShortcut shortcuts
            :: attrs
        )
(onShortcut 함수에 대한 주석에 관심이 있는 분들은 이 article을 보실 수 있습니다)
여기서 주로 사용자 정의 요소에 shortcuts이라는 속성을 설정합니다. 이 속성은 shortcutElement 함수에 전달되는 모든 단축키를 포함하고 shortcut 이벤트를 정탐하여 단축키의 이름을 추출하고 보내야 할 메시지를 찾습니다.
마지막으로 느릅나무의 한 면은 보기에 매우 간단하지 않습니까?

Huston, JavaScript입니다. 받았습니까?


JavaScript의 2 TODO으로 돌아가기
  • 단축키에 일치하는 항목이 있는지 찾아내고 요소는
  • 을 탐지해야 한다
  • 이벤트가 있으면 이벤트를 보냅니다.
  • elm 부분에서 shortcuts 속성을 설정하기 때문에, 우리는 this.shortcuts 클래스에서 ShortcutElement을 통해 이 그룹에 간단하게 접근할 수 있습니다.그리고 단축키에 대한 작은 경고는 어떤 키를 눌렀는지 확인해야 한다는 것입니다. 예를 들어 ShiftAlto를 누르라고 하면 event.key의 값이 사용자의 입력 방법과 운영체제에 따라 크게 바뀔 수 있습니다(예를 들어 o, Ø,...).
    MDN에서 설명한 바와 같이 사용자가 QWERTY 키보드를 사용하고 있다고 가정하면 event.code을 사용하는 것은 가능하지만 이것은 쓰레기 해결 방안이다.
    반대로, 나는 deburr from lodash을 사용하는 것을 권장한다. 이것은 모든 변음 표기 (즉 눌린 원시 자모로 되돌아오는 것) 를 삭제할 것이다.
    이벤트를 보내는 것은 CustomEvent의 구조 함수를 사용하고 두 번째 파라미터의 detail 부분에 속성을 설정하는 것처럼 간단합니다.
    위에서 말한 바와 같이 우리는 다음과 같은 것을 얻었다.
        this.listener = (evt) => {
          const event = evt
          this.shortcuts
            .filter(
              ({ baseKey, alt, shift, ctrl, meta }) =>
                deburr(event.key).toLowerCase() === baseKey.toLowerCase() &&
                (alt == null || alt === event.altKey) &&
                (shift == null || shift === event.shiftKey) &&
                (ctrl == null || ctrl === event.ctrlKey) &&
                (meta == null || meta === event.metaKey)
            ) // now we have all the shortcuts that match the current event
            .map(({ name }) => {
              event.preventDefault()
              event.stopPropagation()
              this.dispatchEvent(
                new CustomEvent('shortcut', {
                  bubbles: false,
                  detail: {
                    name,
                    event
                  }
                })
              )
            })
        }
    
    그것의 실제 응용을 보려면 Github page here을 보십시오

    Elm의 정점도


    Apex charts은 자바스크립트에 사용되는 기이한 도표 라이브러리로 많은 상호작용 도표 유형과 재미있는 조합 방식을 제공한다.
    내가 Elm에서 이런 라이브러리를 찾았을 때, 내가 찾고 있는 라이브러리를 찾지 못했다. 나는 Apex 도표와 Elm를 통합하기 위해 사용자 정의 요소를 만들 것이라고 생각한다.
    마지막으로, 개발자는 다음과 같은 내용을 작성할 수 있습니다.
    Apex.chart
        |> Apex.addLineSeries "Connections by week" (connectionsByWeek logins)
        |> Apex.addColumnSeries "Connections within office hour for that week" (dayTimeConnectionByWeek logins)
        |> Apex.addColumnSeries "Connections outside office hour for that week" (outsideOfficeHourConnectionByWeek logins)
        |> Apex.withXAxisType Apex.DateTime
    
    그리고 아주 좋은 도표를 받았는데, 한 줄에 두 열이 있었다.
    이 글은 이미 매우 길기 때문에, 나는 두 번째 사용자 정의 요소를 한동안 보류할 것입니다. 그러나, 당신은 그것의primer here (코드는 here) 을 가지고 있을 수 있습니다.
    작업을 수행하기 위해서는 JavaScript의 gettersetter을 자세히 검토하여 시간에 따라 변하는 속성을 처리해야 합니다(사용자 정의 요소의 생명 주기 내).

    좋은 웹페이지 즐겨찾기