@globalbrain/sefirot 4.25.1 → 4.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -104,6 +104,7 @@
104
104
  }
105
105
 
106
106
  .SContent :deep(ol > li::before) {
107
+ position: absolute;
107
108
  margin-right: 3px;
108
109
  margin-left: -20px;
109
110
  color: var(--c-text-1);
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { provideDataListState } from '../composables/DataList'
4
+
5
+ const props = withDefaults(defineProps<{
6
+ labelWidth?: string
7
+ }>(), {
8
+ labelWidth: '128px'
9
+ })
10
+
11
+ provideDataListState({
12
+ labelWidth: computed(() => props.labelWidth)
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <div class="SDataList">
18
+ <slot />
19
+ </div>
20
+ </template>
21
+
22
+ <style scoped lang="postcss">
23
+ .SDataList :deep(.SDataListItem::after),
24
+ .SDataList :deep(.SDataListItem:first-child::before) {
25
+ display: block;
26
+ border-top: 1px dashed var(--c-divider);
27
+ width: 100%;
28
+ content: "";
29
+ }
30
+ </style>
@@ -0,0 +1,137 @@
1
+ <script setup lang="ts">
2
+ import { Comment, Text, computed, useSlots } from 'vue'
3
+ import { useDataListState } from '../composables/DataList'
4
+
5
+ const props = withDefaults(defineProps<{
6
+ dir?: 'row' | 'column'
7
+ maxWidth?: string
8
+ align?: 'left' | 'right'
9
+ preWrap?: boolean
10
+ lineClamp?: string | number
11
+ tnum?: boolean
12
+ }>(), {
13
+ dir: 'row',
14
+ maxWidth: '100%'
15
+ })
16
+
17
+ const {
18
+ labelWidth
19
+ } = useDataListState()
20
+
21
+ const slots = useSlots()
22
+
23
+ const classes = computed(() => [
24
+ props.dir,
25
+ props.align,
26
+ { 'pre-wrap': props.preWrap },
27
+ { 'line-clamp': !!props.lineClamp },
28
+ { tnum: props.tnum }
29
+ ])
30
+
31
+ const labelStyles = computed(() => ({
32
+ width: props.dir === 'row' ? labelWidth.value : '100%'
33
+ }))
34
+
35
+ const valueStyles = computed(() => ({
36
+ 'max-width': props.maxWidth,
37
+ '-webkit-line-clamp': props.lineClamp
38
+ }))
39
+
40
+ const hasValue = computed(() => hasSlotContent('value'))
41
+
42
+ function hasSlotContent(name = 'default'): boolean {
43
+ return !!slots[name]?.().some((s) => {
44
+ if (s.type === Comment) {
45
+ return false
46
+ }
47
+ if (s.type === Text && typeof s.children === 'string') {
48
+ return !!s.children.trim()
49
+ }
50
+ return true
51
+ })
52
+ }
53
+ </script>
54
+
55
+ <template>
56
+ <div class="SDataListItem" :class="classes">
57
+ <div class="content">
58
+ <div class="label" :style="labelStyles">
59
+ <slot name="label" />
60
+ </div>
61
+ <div v-if="!hasValue" class="empty">
62
+
63
+ </div>
64
+ <div v-else-if="hasValue" class="value" :style="valueStyles">
65
+ <slot name="value" />
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </template>
70
+
71
+ <style scoped lang="postcss">
72
+ .SDataListItem {
73
+ width: 100%;
74
+ max-width: 100%;
75
+ }
76
+
77
+ .content {
78
+ display: flex;
79
+ padding: 10px 0;
80
+ min-height: 48px;
81
+ }
82
+
83
+ .label {
84
+ flex-shrink: 0;
85
+ padding: 2px 0;
86
+ line-height: 24px;
87
+ font-size: 14px;
88
+ color: var(--c-text-2);
89
+ }
90
+
91
+ .empty {
92
+ flex-grow: 1;
93
+ padding: 2px 0;
94
+ line-height: 24px;
95
+ font-size: 14px;
96
+ color: var(--c-text-3);
97
+ }
98
+
99
+ .value {
100
+ flex-grow: 1;
101
+ padding: 2px 0;
102
+ line-height: 24px;
103
+ font-size: 14px;
104
+ color: var(--c-text-1);
105
+ }
106
+
107
+ .SDataListItem.row .content {
108
+ flex-direction: row;
109
+ }
110
+
111
+ .SDataListItem.column .content {
112
+ flex-direction: column;
113
+ }
114
+
115
+ .SDataListItem.left .value {
116
+ text-align: left;
117
+ }
118
+
119
+ .SDataListItem.right .value {
120
+ text-align: right;
121
+ }
122
+
123
+ .SDataListItem.pre-wrap .value {
124
+ white-space: pre-wrap;
125
+ }
126
+
127
+ .SDataListItem.line-clamp .value {
128
+ display: -webkit-box;
129
+ -webkit-box-orient: vertical;
130
+ text-overflow: ellipsis;
131
+ overflow: hidden;
132
+ }
133
+
134
+ .SDataListItem.tnum .value {
135
+ font-feature-settings: "tnum";
136
+ }
137
+ </style>
@@ -141,6 +141,7 @@ function handleArray(value: OptionValue) {
141
141
  :check-text
142
142
  :check-color
143
143
  :validation
144
+ :hide-error
144
145
  >
145
146
  <div class="container" ref="container">
146
147
  <div
@@ -61,6 +61,7 @@ function onSelect(value: T) {
61
61
  <SInputBase
62
62
  class="SInputSegments"
63
63
  :class="[size ?? 'small', { block }]"
64
+ :size
64
65
  :label
65
66
  :note
66
67
  :info
@@ -1,18 +1,18 @@
1
1
  <script setup lang="ts">
2
2
  import { type Component, computed } from 'vue'
3
- import { useMarkdown } from '../composables/Markdown'
3
+ import { type UseMarkdownOptions, useMarkdown } from '../composables/Markdown'
4
4
 
5
- const props = withDefaults(defineProps<{
5
+ export interface SMarkdownProps extends UseMarkdownOptions {
6
6
  tag?: Component | string
7
7
  content: string
8
- html?: boolean
9
- inline?: boolean
10
- }>(), {
8
+ }
9
+
10
+ const props = withDefaults(defineProps<SMarkdownProps>(), {
11
11
  tag: 'div',
12
12
  html: true
13
13
  })
14
14
 
15
- const markdown = useMarkdown({ html: props.html, inline: props.inline })
15
+ const markdown = useMarkdown(props)
16
16
  const rendered = computed(() => markdown(props.content))
17
17
  </script>
18
18
 
@@ -61,6 +61,13 @@ const cellOfColToGrow = computed(() => {
61
61
  const colWidths = reactive<Record<string, string>>({})
62
62
  const blockWidth = ref<number | undefined>()
63
63
 
64
+ watch(() => unref(props.options.columns), (columns) => {
65
+ Object.keys(columns).forEach((key) => {
66
+ const width = columns[key]?.width
67
+ if (width) { colWidths[key] = width }
68
+ })
69
+ }, { immediate: true, deep: true, flush: 'pre' })
70
+
64
71
  const showHeader = computed(() => {
65
72
  const header = unref(props.options.header)
66
73
 
@@ -0,0 +1,23 @@
1
+ import { type ComputedRef, inject, provide } from 'vue'
2
+
3
+ export interface DataListState {
4
+ labelWidth: ComputedRef<string>
5
+ }
6
+
7
+ export const DataListStateKey = 'sefirot-data-list-state-key'
8
+
9
+ export function provideDataListState(state: DataListState): void {
10
+ provide(DataListStateKey, state)
11
+ }
12
+
13
+ export function useDataListState(): DataListState {
14
+ const state = inject<DataListState | null>(DataListStateKey, null) || null
15
+ if (!state) {
16
+ throw new Error(
17
+ 'Unexpected call to `useDataListState`. This probably means you are using'
18
+ + '`<DDataList>` child component outside of `<DDataList>`. Make sure'
19
+ + ' to wrap the component within `<DDataList>` component.'
20
+ )
21
+ }
22
+ return state
23
+ }
@@ -1,8 +1,71 @@
1
1
  import DOMPurify, { type Config } from 'dompurify'
2
- import MarkdownIt, { type Options as MarkdownItOptions } from 'markdown-it'
2
+ import MarkdownIt from 'markdown-it'
3
3
 
4
4
  export type UseMarkdown = (source: string, inline?: boolean) => string
5
5
 
6
+ // vendored for vue compatibility
7
+ export interface MarkdownItOptions {
8
+ /**
9
+ * Set `true` to enable HTML tags in source. Be careful!
10
+ * That's not safe! You may need external sanitizer to protect output from XSS.
11
+ * It's better to extend features via plugins, instead of enabling HTML.
12
+ * @default false
13
+ */
14
+ html?: boolean | undefined
15
+
16
+ /**
17
+ * Set `true` to add '/' when closing single tags
18
+ * (`<br />`). This is needed only for full CommonMark compatibility. In real
19
+ * world you will need HTML output.
20
+ * @default false
21
+ */
22
+ xhtmlOut?: boolean | undefined
23
+
24
+ /**
25
+ * Set `true` to convert `\n` in paragraphs into `<br>`.
26
+ * @default false
27
+ */
28
+ breaks?: boolean | undefined
29
+
30
+ /**
31
+ * CSS language class prefix for fenced blocks.
32
+ * Can be useful for external highlighters.
33
+ * @default 'language-'
34
+ */
35
+ langPrefix?: string | undefined
36
+
37
+ /**
38
+ * Set `true` to autoconvert URL-like text to links.
39
+ * @default false
40
+ */
41
+ linkify?: boolean | undefined
42
+
43
+ /**
44
+ * Set `true` to enable [some language-neutral replacement](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.js) +
45
+ * quotes beautification (smartquotes).
46
+ * @default false
47
+ */
48
+ typographer?: boolean | undefined
49
+
50
+ /**
51
+ * Double + single quotes replacement
52
+ * pairs, when typographer enabled and smartquotes on. For example, you can
53
+ * use `'«»„“'` for Russian, `'„“‚‘'` for German, and
54
+ * `['«\xA0', '\xA0»', '‹\xA0', '\xA0›']` for French (including nbsp).
55
+ * @default '“”‘’'
56
+ */
57
+ quotes?: string | string[]
58
+
59
+ /**
60
+ * Highlighter function for fenced code blocks.
61
+ * Highlighter `function (str, lang, attrs)` should return escaped HTML. It can
62
+ * also return empty string if the source was not changed and should be escaped
63
+ * externally. If result starts with <pre... internal wrapper is skipped.
64
+ * @default null
65
+ */
66
+ highlight?: ((str: string, lang: string, attrs: string) => string) | null | undefined
67
+ }
68
+
6
69
  export interface UseMarkdownOptions extends MarkdownItOptions {
7
70
  config?: (md: MarkdownIt) => void
8
71
  /** @default false */
@@ -40,6 +103,16 @@ export function useMarkdown({
40
103
  //
41
104
 
42
105
  const md = new MarkdownIt({ html: true, linkify: true, ...options })
106
+
107
+ md.renderer.rules.ordered_list_open = (tokens, idx, options, env, self) => {
108
+ const token = tokens[idx]
109
+ const start = Number(token.attrGet('start')) - 1
110
+ if (start >= 0) {
111
+ token.attrSet('style', `counter-reset: s-medium-counter ${start}`)
112
+ }
113
+ return self.renderToken(tokens, idx, options)
114
+ }
115
+
43
116
  config?.(md)
44
117
 
45
118
  return (source, inline = _inline) => {
@@ -45,6 +45,7 @@ export interface TableColumn<V, R, SV, SR> {
45
45
  className?: string
46
46
  dropdown?: DropdownSection[]
47
47
  freeze?: boolean
48
+ width?: string
48
49
  grow?: boolean
49
50
  resizable?: boolean
50
51
  cell?: TableCell<V, R> | TableColumnCellFn<V, R>
@@ -0,0 +1,15 @@
1
+ import { type App } from 'vue'
2
+ import SDataList from '../components/SDataList.vue'
3
+ import SDataListItem from '../components/SDataListItem.vue'
4
+
5
+ export function mixin(app: App): void {
6
+ app.component('SDataList', SDataList)
7
+ app.component('SDataListItem', SDataListItem)
8
+ }
9
+
10
+ declare module 'vue' {
11
+ export interface GlobalComponents {
12
+ SDataList: typeof SDataList
13
+ SDataListItem: typeof SDataListItem
14
+ }
15
+ }
@@ -6,6 +6,7 @@ import SModal from '../components/SModal.vue'
6
6
  import STrans from '../components/STrans.vue'
7
7
  import { mixin as mixinCard } from './Card'
8
8
  import { mixin as mixinControl } from './Control'
9
+ import { mixin as mixinDataList } from './DataList'
9
10
  import { mixin as mixinDesc } from './Desc'
10
11
  import { mixin as mixinDoc } from './Doc'
11
12
  import { mixin as mixinGrid } from './Grid'
@@ -14,6 +15,7 @@ import { mixin as mixinHead } from './Head'
14
15
  export function mixin(app: App): void {
15
16
  mixinCard(app)
16
17
  mixinControl(app)
18
+ mixinDataList(app)
17
19
  mixinDesc(app)
18
20
  mixinDoc(app)
19
21
  mixinGrid(app)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
3
  "type": "module",
4
- "version": "4.25.1",
4
+ "version": "4.27.0",
5
5
  "packageManager": "pnpm@9.15.4",
6
6
  "description": "Vue Components for Global Brain Design System.",
7
7
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",