Skip to content

Commit

Permalink
release(logcat): v0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
surunzi committed Oct 29, 2024
1 parent 100a0bd commit e5fdc48
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ UI library.
* [json-editor](./src/json-editor/README.md): JSON editor.
* [keyboard](./src/keyboard/README.md): Virtual keyboard.
* [log](./src/log/README.md): Terminal log viewer.
* [logcat](./src/logcat/README.md): Android logcat viewer.
* [markdown-editor](./src/markdown-editor/README.md): Markdown editor with preview.
* [markdown-viewer](./src/markdown-viewer/README.md): Live markdown renderer.
* [mask-editor](./src/mask-editor/README.md): Image mask editing.
Expand Down
2 changes: 1 addition & 1 deletion index.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,12 @@
"react": false
},
"logcat": {
"react": true,
"version": "0.1.0",
"style": true,
"icon": false,
"test": true,
"install": false,
"react": false,
"dependencies": []
},
"lrc-player": {
Expand Down
40 changes: 40 additions & 0 deletions src/logcat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,43 @@ npm install luna-logcat --save
import 'luna-logcat/luna-logcat.css'
import LunaLogcat from 'luna-logcat'
```

## Usage

```javascript
const logcat = new LunaLogcat(container)
logcatp.append({
date: '2021-01-01 00:00:00',
package: 'com.example',
pid: 1234,
tid: 1234,
priority: 3,
tag: 'tag',
message: 'message',
})
```

## Configuration

* entries(IEntry[]): Log entries.
* filter(IFilter): Log filter.
* maxNum(number): Max entry number, zero means infinite.
* wrapLongLines(boolean): Wrap long lines.

## Api

### append(entry: IEntry): void

Append entry.

### clear(): void

Clear all entries.

## Types

### IFilter

* package(string): Package name.
* priority(number): Entry priority.
* tag(string): Tag name.
80 changes: 77 additions & 3 deletions src/logcat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import throttle from 'licia/throttle'
import isDate from 'licia/isDate'
import each from 'licia/each'
import strHash from 'licia/strHash'
import $ from 'licia/$'
import lowerCase from 'licia/lowerCase'
import contain from 'licia/contain'
import dateFormat from 'licia/dateFormat'
import { exportCjs } from '../share/util'

/** IOptions */
export interface IOptions extends IComponentOptions {
/** Max entry number, zero means infinite. */
maxNum?: number
/** Log filter. */
filter?: IFilter
/** Log entries. */
Expand All @@ -19,8 +24,14 @@ export interface IOptions extends IComponentOptions {
wrapLongLines?: boolean
}

interface IFilter {
/** IFilter */
export interface IFilter {
/** Entry priority. */
priority?: number
/** Package name. */
package?: string
/** Tag name. */
tag?: string
}

interface IBaseEntry {
Expand Down Expand Up @@ -52,6 +63,7 @@ interface IEntry extends IBaseEntry {
* })
*/
export default class Logcat extends Component<IOptions> {
private isAtBottom = true
private render: types.AnyFn
private entries: Array<{
container: HTMLElement
Expand All @@ -61,6 +73,7 @@ export default class Logcat extends Component<IOptions> {
super(container, { compName: 'logcat' })

this.initOptions(options, {
maxNum: 0,
entries: [],
wrapLongLines: false,
})
Expand All @@ -78,8 +91,14 @@ export default class Logcat extends Component<IOptions> {

this.bindEvent()
}
destroy() {
this.$container.off('scroll', this.onScroll)
super.destroy()
}
/** Append entry. */
append(entry: IEntry) {
const { c } = this
const { c, entries } = this
const { maxNum } = this.options

const date: Date = isDate(entry.date)
? (entry.date as Date)
Expand All @@ -92,11 +111,18 @@ export default class Logcat extends Component<IOptions> {
...entry,
date,
}
this.entries.push({
entries.push({
container,
entry: e,
})

if (maxNum !== 0 && entries.length > maxNum) {
const entry = entries.shift()
if (entry) {
$(entry.container).remove()
}
}

const html = [
`<span class="${c('date')}">${dateFormat(
date,
Expand All @@ -114,12 +140,23 @@ export default class Logcat extends Component<IOptions> {

if (this.filterEntry(e)) {
this.container.appendChild(container)
if (this.isAtBottom) {
this.scrollToBottom()
}
}
}
/** Clear all entries. */
clear() {
this.entries = []
this.$container.html('')
}
private scrollToBottom() {
const { container } = this
const { scrollHeight, scrollTop, offsetHeight } = container
if (scrollTop <= scrollHeight - offsetHeight) {
container.scrollTop = 10000000
}
}
private filterEntry(entry: IBaseEntry) {
const { filter } = this.options

Expand All @@ -131,12 +168,26 @@ export default class Logcat extends Component<IOptions> {
return false
}

if (filter.package) {
if (!contain(lowerCase(entry.package), lowerCase(filter.package))) {
return false
}
}

if (filter.tag) {
if (!contain(lowerCase(entry.tag), lowerCase(filter.tag))) {
return false
}
}

return true
}
private bindEvent() {
const { c } = this

this.on('optionChange', (name, val) => {
const { entries } = this

switch (name) {
case 'wrapLongLines':
if (val) {
Expand All @@ -145,21 +196,44 @@ export default class Logcat extends Component<IOptions> {
this.$container.rmClass(c('wrap-long-lines'))
}
break
case 'maxNum':
if (val > 0 && entries.length > val) {
this.entries = entries.slice(entries.length - val)
this.render()
}
break
case 'filter':
this.render()
break
}
})

this.$container.on('scroll', this.onScroll)
}
private onScroll = () => {
const { scrollHeight, offsetHeight, scrollTop } = this
.container as HTMLElement

let isAtBottom = false
if (scrollHeight === offsetHeight) {
isAtBottom = true
} else if (Math.abs(scrollHeight - offsetHeight - scrollTop) < 1) {
isAtBottom = true
}
this.isAtBottom = isAtBottom
}
private _render() {
const { container } = this
this.$container.html('')
this.isAtBottom = true

each(this.entries, (entry) => {
if (this.filterEntry(entry.entry)) {
container.appendChild(entry.container)
}
})

this.scrollToBottom()
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/logcat/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"name": "logcat",
"version": "0.1.0",
"description": "Android logcat viewer"
"description": "Android logcat viewer",
"luna": {
"react": true
}
}
46 changes: 46 additions & 0 deletions src/logcat/react.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { CSSProperties, FC, useEffect, useRef } from 'react'
import each from 'licia/each'
import Logcat, { IOptions } from './index'
import { useNonInitialEffect } from '../share/hooks'

interface IILogcatProps extends IOptions {
style?: CSSProperties
className?: string
onCreate?: (logcat: Logcat) => void
}

const LunaLogcat: FC<IILogcatProps> = (props) => {
const logcatRef = useRef<HTMLDivElement>(null)
const logcat = useRef<Logcat>()

useEffect(() => {
const { maxNum, wrapLongLines, filter, entries } = props
logcat.current = new Logcat(logcatRef.current!, {
filter,
maxNum,
wrapLongLines,
entries,
})
props.onCreate && props.onCreate(logcat.current)

return () => logcat.current?.destroy()
}, [])

each(['filter', 'maxNum', 'wrapLongLines'], (key: keyof IOptions) => {
useNonInitialEffect(() => {
if (logcat.current) {
logcat.current.setOption(key, props[key])
}
}, [props[key]])
})

return (
<div
className={props.className || ''}
ref={logcatRef}
style={props.style}
></div>
)
}

export default LunaLogcat
32 changes: 29 additions & 3 deletions src/logcat/story.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import readme from './README.md'
import story from '../share/story'
import each from 'licia/each'
import $ from 'licia/$'
import { boolean, number, select } from '@storybook/addon-knobs'
import { boolean, number, select, text } from '@storybook/addon-knobs'
import LunaLogcat from './react'
import logs from './logcat.json'

const def = story(
'logcat',
(container) => {
$(container).css('height', '500px')

const { wrapLongLines, filter } = createKnobs()
const { wrapLongLines, maxNum, filter } = createKnobs()

const logcat = new Logcat(container, {
filter,
maxNum,
wrapLongLines,
})
each(logs, (log) => logcat.append(log))
Expand All @@ -25,6 +27,19 @@ const def = story(
{
readme,
story: __STORY__,
ReactComponent() {
const { wrapLongLines, maxNum, filter } = createKnobs()

return (
<LunaLogcat
style={{ height: '500px' }}
wrapLongLines={wrapLongLines}
maxNum={maxNum}
filter={filter}
onCreate={(logcat) => each(logs, (log) => logcat.append(log))}
/>
)
},
}
)

Expand All @@ -40,15 +55,26 @@ function createKnobs() {
},
1
)
const filterPackage = text('Filter Package', '')
const filterTag = text('Filter Tag', '')
const maxNum = number('Max Number', 500, {
range: true,
min: 10,
max: 1000,
step: 10,
})
const wrapLongLines = boolean('Wrap Long Lines', false)

return {
filter: {
priority: filterPriority,
package: filterPackage,
tag: filterTag,
},
maxNum,
wrapLongLines,
}
}

export default def
export const { logcat } = def
export const { logcat: html, react } = def

0 comments on commit e5fdc48

Please sign in to comment.