Skip to content

Commit

Permalink
feat: convert vanila-jsx to Solid
Browse files Browse the repository at this point in the history
  • Loading branch information
aminya committed Jun 16, 2021
1 parent e46356f commit 2f55fc1
Show file tree
Hide file tree
Showing 8 changed files with 4,136 additions and 152 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
npm-debug.log
node_modules
dist
.parcel-cache
3 changes: 3 additions & 0 deletions babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["babel-preset-solid"]
}
137 changes: 75 additions & 62 deletions lib/elements/list.tsx
Original file line number Diff line number Diff line change
@@ -1,79 +1,92 @@
/** @jsx jsx */
// eslint-disable-next-line no-unused-vars
import { createClass, jsx } from "vanilla-jsx"
import { createComputed, createEffect, createSignal, createSelector, For } from "solid-js"
import { scrollIntoViewIfNeeded } from "atom-ide-base/src-commons-ui/scrollIntoView"

import { $class } from "../helpers"
import type { ListMovement } from "../types"
import type { ListMovement, ListItem } from "../types"

export default createClass({
renderView(suggestions, selectCallback) {
let className = "select-list popover-list"
export interface Props {
suggestions: Array<ListItem>
selectCallback: (suggestion: ListItem) => void
movement?: ListMovement
select: boolean
}

if (suggestions.length > 7) {
className += " intentions-scroll"
}
export function ListElement(props: Props) {
// current active index
const [getActiveIndex, setActiveIndex] = createSignal(-1)
// current active id
const isSelected = createSelector(getActiveIndex)

this.suggestions = suggestions
this.suggestionsCount = suggestions.length
this.suggestionsIndex = -1
this.selectCallback = selectCallback
return (
<intentions-list class={className} id="intentions-list">
<ol className="list-group" ref="list">
{suggestions.map(function (suggestion) {
return (
<li>
<span
className={suggestion[$class]}
on-click={function () {
selectCallback(suggestion)
}}
>
{suggestion.title}
</span>
</li>
)
})}
</ol>
</intentions-list>
)
},
function handleSelection(active: HTMLElement, suggestion: ListItem, index: number) {
// scroll into for the current selected element
scrollIntoViewIfNeeded(active, false)
// call its associated callback
props.selectCallback(suggestion)
// store it in the signal
setActiveIndex(index)
}

move(movement: ListMovement) {
let newIndex = this.suggestionsIndex
function handleMove() {
const suggestionsCount = props.suggestions.length

if (movement === "up") {
newIndex--
} else if (movement === "down") {
newIndex++
} else if (movement === "move-to-top") {
newIndex = 0
} else if (movement === "move-to-bottom") {
newIndex = this.suggestionsCount
let index = getActiveIndex()

if (props.movement === "up") {
index--
} else if (props.movement === "down") {
index++
} else if (props.movement === "move-to-top") {
index = 0
} else if (props.movement === "move-to-bottom") {
index = suggestionsCount
}

// TODO: Implement page up/down
newIndex %= this.suggestionsCount
index %= suggestionsCount

if (newIndex < 0) {
newIndex = this.suggestionsCount + newIndex
if (index < 0) {
index = suggestionsCount + index
}

this.selectIndex(newIndex)
},
setActiveIndex(index)
}

selectIndex(index) {
if (this.refs.active) {
this.refs.active.classList.remove("selected")
createComputed(() => {
handleMove()
})
createEffect(() => {
if (props.select) {
props.selectCallback(props.suggestions[getActiveIndex()])
}
})

this.refs.active = this.refs.list.children[index]
this.refs.active.classList.add("selected")
this.refs.active.scrollIntoViewIfNeeded(false)
this.suggestionsIndex = index
},
let className = "select-list popover-list"
if (props.suggestions.length > 7) {
className += " intentions-scroll"
}

select() {
this.selectCallback(this.suggestions[this.suggestionsIndex])
},
})
return (
<div class={className} id="intentions-list">
<ol className="list-group">
<For each={props.suggestions}>
{(suggestion, getIndex) => {
const index = getIndex()
let liRef: HTMLLIElement | undefined
return (
<li ref={liRef} class={isSelected(index) ? "selected" : ""}>
<span
className={suggestion[$class]}
onClick={() => {
handleSelection(liRef!, suggestion, index)
}}
>
{suggestion.title}
</span>
</li>
)
}}
</For>
</ol>
</div>
)
}
5 changes: 3 additions & 2 deletions lib/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
"inlineSources": true,
"preserveSymlinks": true,
"removeComments": false,
"jsx": "react",
"jsxFactory": "jsx",
"isolatedModules": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"lib": ["ES2018", "dom"],
"target": "ES2018",
"allowJs": true,
Expand Down
34 changes: 23 additions & 11 deletions lib/view-list.ts → lib/view-list.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
import { createMutable } from "solid-js"
import { render } from "solid-js/web"
import { CompositeDisposable, Emitter } from "sb-event-kit"
import type { Disposable } from "sb-event-kit"
import type { TextEditor } from "atom"

import ListElement from "./elements/list"
import { Props as ListElementProps, ListElement } from "./elements/list"
import type { ListItem, ListMovement } from "./types"

export default class ListView {
emitter: Emitter
element: typeof ListElement
element: HTMLElement
subscriptions: CompositeDisposable

// the props of ListElement component (this is reactive)
listElementProps: ListElementProps = createMutable({
suggestions: [],
selectCallback: (selectedSuggestion: ListItem) => {
this.emitter.emit("did-select", selectedSuggestion)
this.dispose()
},
movement: "move-to-top",
select: false,
})

constructor() {
this.emitter = new Emitter()
this.element = new ListElement()
this.element = document.createElement("div")
this.subscriptions = new CompositeDisposable()
this.subscriptions.add(this.emitter)
this.subscriptions.add(this.element)
}

activate(editor: TextEditor, suggestions: Array<ListItem>) {
this.element.render(suggestions, (selected) => {
this.emitter.emit("did-select", selected)
this.dispose()
})
this.element.move("move-to-top")
this.listElementProps.suggestions = suggestions

// render the list element component
render(() => ListElement(this.listElementProps), this.element)

const bufferPosition = editor.getCursorBufferPosition()
const marker = editor.markBufferRange([bufferPosition, bufferPosition], {
invalidate: "never",
Expand All @@ -38,11 +50,11 @@ export default class ListView {
}

move(movement: ListMovement) {
this.element.move(movement)
this.listElementProps.movement = movement
}

select() {
this.element.select()
this.listElementProps.select = true
}

onDidSelect(callback: (...args: Array<any>) => any): Disposable {
Expand Down
32 changes: 30 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "intentions",
"main": "./dist/index.js",
"source": "./lib/index.ts",
"version": "2.0.0",
"description": "Base package for showing intentions in Atom",
"keywords": [],
Expand All @@ -10,16 +11,21 @@
"atom": ">=1.0.0 <2.0.0"
},
"dependencies": {
"atom-ide-base": "^3.0.0",
"disposable-event": "^2.0.0",
"sb-event-kit": "^3.0.0",
"solid-js": "^0.26.5",
"vanilla-jsx": "^3.0.2"
},
"devDependencies": {
"@types/atom": "latest",
"@types/jasmine": "v1",
"babel-preset-solid": "^0.26.5",
"build-commit": "^0.1.4",
"cross-env": "^7.0.3",
"eslint-config-atomic": "^1.16.0",
"jasmine-fix": "^1.3.1",
"parcel": "^2.0.0-beta.3.1",
"prettier-config-atomic": "^2.0.5",
"typescript": "^4.3.2"
},
Expand All @@ -42,8 +48,30 @@
"lint": "eslint . --fix",
"test.lint": "eslint .",
"test": "npm run build && atom --test spec",
"dev": "tsc -p ./lib/tsconfig.json --watch",
"build": "tsc -p ./lib/tsconfig.json || echo done",
"clean": "shx rm -rf ./dist",
"types": "tsc -p ./lib/tsconfig.json --noEmit",
"dev": "cross-env NODE_ENV=development parcel watch --target main ./lib/index.ts",
"build": "cross-env NODE_ENV=production parcel build --target main ./lib/index.ts --detailed-report",
"build-commit": "build-commit -o dist"
},
"targets": {
"main": {
"context": "electron-renderer",
"includeNodeModules": {
"atom": false,
"electron": false,
"disposable-event": false
},
"outputFormat": "commonjs",
"isLibrary": true
}
},
"alias": {
"solid-js": "solid-js/dist/solid.js",
"solid-js/web": "solid-js/web/dist/web.js"
},
"_moduleAliases": {
"solid-js": "./node_modules/solid-js/dist/solid.cjs",
"solid-js/web": "./node_modules/solid-js/web/dist/web.cjs"
}
}
Loading

0 comments on commit 2f55fc1

Please sign in to comment.