@didaoktv/didaoui-uniapp 1.0.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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +89 -0
  3. package/components/dd-action-sheet/dd-action-sheet.vue +207 -0
  4. package/components/dd-alert/dd-alert.vue +240 -0
  5. package/components/dd-avatar/dd-avatar.vue +168 -0
  6. package/components/dd-backtop/dd-backtop.vue +75 -0
  7. package/components/dd-badge/dd-badge.vue +150 -0
  8. package/components/dd-button/dd-button.vue +215 -0
  9. package/components/dd-capsule-button/dd-capsule-button.vue +109 -0
  10. package/components/dd-card/dd-card.vue +155 -0
  11. package/components/dd-cell/dd-cell.vue +201 -0
  12. package/components/dd-cell-group/dd-cell-group.vue +56 -0
  13. package/components/dd-champion-card/dd-champion-card.vue +258 -0
  14. package/components/dd-checkbox/dd-checkbox.vue +145 -0
  15. package/components/dd-collapse/dd-collapse.vue +63 -0
  16. package/components/dd-collapse-item/dd-collapse-item.vue +154 -0
  17. package/components/dd-count-down/dd-count-down.vue +163 -0
  18. package/components/dd-date-picker/dd-date-picker.vue +253 -0
  19. package/components/dd-dialog/dd-dialog.vue +264 -0
  20. package/components/dd-divider/dd-divider.vue +104 -0
  21. package/components/dd-drawer/dd-drawer.vue +214 -0
  22. package/components/dd-dropdown-item/dd-dropdown-item.vue +203 -0
  23. package/components/dd-dropdown-menu/dd-dropdown-menu.vue +177 -0
  24. package/components/dd-empty-state/dd-empty-state.vue +140 -0
  25. package/components/dd-feature-grid/dd-feature-grid.vue +139 -0
  26. package/components/dd-field/dd-field.vue +240 -0
  27. package/components/dd-icon/dd-icon.vue +464 -0
  28. package/components/dd-image/dd-image.vue +158 -0
  29. package/components/dd-input/dd-input.vue +172 -0
  30. package/components/dd-list-cell/dd-list-cell.vue +152 -0
  31. package/components/dd-loading/dd-loading.vue +187 -0
  32. package/components/dd-loadmore/dd-loadmore.vue +152 -0
  33. package/components/dd-mini-program-navbar/dd-mini-program-navbar.vue +245 -0
  34. package/components/dd-modal/dd-modal.vue +286 -0
  35. package/components/dd-navigation/dd-navigation.vue +134 -0
  36. package/components/dd-overlay/dd-overlay.vue +114 -0
  37. package/components/dd-picker/dd-picker.vue +203 -0
  38. package/components/dd-popover/dd-popover.vue +190 -0
  39. package/components/dd-popover-item/dd-popover-item.vue +88 -0
  40. package/components/dd-popup/dd-popup.vue +243 -0
  41. package/components/dd-progress/dd-progress.vue +255 -0
  42. package/components/dd-pull-refresh/dd-pull-refresh.vue +232 -0
  43. package/components/dd-radio/dd-radio.vue +129 -0
  44. package/components/dd-rate/dd-rate.vue +133 -0
  45. package/components/dd-room-card/dd-room-card.vue +309 -0
  46. package/components/dd-search-bar/dd-search-bar.vue +176 -0
  47. package/components/dd-segmented-tab/dd-segmented-tab.vue +187 -0
  48. package/components/dd-skeleton/dd-skeleton.vue +184 -0
  49. package/components/dd-slider/dd-slider.vue +227 -0
  50. package/components/dd-stat-card/dd-stat-card.vue +174 -0
  51. package/components/dd-step/dd-step.vue +188 -0
  52. package/components/dd-stepper/dd-stepper.vue +171 -0
  53. package/components/dd-steps/dd-steps.vue +61 -0
  54. package/components/dd-sticky/dd-sticky.vue +132 -0
  55. package/components/dd-swipe/dd-swipe.vue +146 -0
  56. package/components/dd-swipe-action/dd-swipe-action.vue +248 -0
  57. package/components/dd-swipe-item/dd-swipe-item.vue +27 -0
  58. package/components/dd-swipeable-tab/dd-swipeable-tab.vue +202 -0
  59. package/components/dd-switch/dd-switch.vue +106 -0
  60. package/components/dd-tabbar/dd-tabbar.vue +112 -0
  61. package/components/dd-tabbar-item/dd-tabbar-item.vue +122 -0
  62. package/components/dd-tag/dd-tag.vue +178 -0
  63. package/components/dd-toast/dd-toast.vue +195 -0
  64. package/components/dd-top-navbar/dd-top-navbar.vue +184 -0
  65. package/components/dd-upload/dd-upload.vue +187 -0
  66. package/index.ts +127 -0
  67. package/libs/utils.ts +106 -0
  68. package/package.json +58 -0
  69. package/scss/_functions.scss +68 -0
  70. package/scss/_mixins.scss +154 -0
  71. package/scss/_reset.scss +30 -0
  72. package/scss/_variables.scss +317 -0
  73. package/uni.scss +4 -0
@@ -0,0 +1,158 @@
1
+ <template>
2
+ <view class="dd-image" :class="{ 'dd-image--round': round }" :style="containerStyle" @click="onClick">
3
+ <image
4
+ class="dd-image__img"
5
+ :src="src"
6
+ :mode="fitMode"
7
+ :lazy-load="lazyLoad"
8
+ :show-menu-by-longpress="showMenuByLongpress"
9
+ @load="onLoad"
10
+ @error="onError"
11
+ />
12
+ <view v-if="status === 'loading'" class="dd-image__mask">
13
+ <slot name="loading">
14
+ <view class="dd-image__spinner"></view>
15
+ </slot>
16
+ </view>
17
+ <view v-if="status === 'error'" class="dd-image__mask">
18
+ <slot name="error">
19
+ <dd-icon name="photo-fail" class="dd-image__error-icon" />
20
+ </slot>
21
+ </view>
22
+ </view>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ import { computed, ref, watch } from 'vue'
27
+ import DdIcon from '../dd-icon/dd-icon.vue'
28
+
29
+ type Fit = 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'
30
+
31
+ interface Props {
32
+ src?: string
33
+ fit?: Fit
34
+ width?: string | number
35
+ height?: string | number
36
+ radius?: string | number
37
+ round?: boolean
38
+ lazyLoad?: boolean
39
+ showMenuByLongpress?: boolean
40
+ }
41
+
42
+ const props = withDefaults(defineProps<Props>(), {
43
+ src: '',
44
+ fit: 'cover',
45
+ width: '100%',
46
+ height: '100%',
47
+ radius: 0,
48
+ round: false,
49
+ lazyLoad: false,
50
+ showMenuByLongpress: false,
51
+ })
52
+
53
+ const emit = defineEmits<{
54
+ (e: 'load', ev: any): void
55
+ (e: 'error', ev: any): void
56
+ (e: 'click', ev: any): void
57
+ }>()
58
+
59
+ type Status = 'loading' | 'loaded' | 'error'
60
+ const status = ref<Status>(props.src ? 'loading' : 'error')
61
+
62
+ // ponytail: scale-down 降级为 aspectFit —— uni image 无原生 scale-down 等价 mode
63
+ const fitMode = computed(() => {
64
+ const map: Record<Fit, string> = {
65
+ fill: 'scaleToFill',
66
+ contain: 'aspectFit',
67
+ cover: 'aspectFill',
68
+ none: 'center',
69
+ 'scale-down': 'aspectFit',
70
+ }
71
+ return map[props.fit]
72
+ })
73
+
74
+ function toSize(v: string | number): string {
75
+ return typeof v === 'number' ? `${v}rpx` : v
76
+ }
77
+
78
+ const containerStyle = computed(() => {
79
+ const radius = props.round ? '50%' : toSize(props.radius)
80
+ return {
81
+ width: toSize(props.width),
82
+ height: toSize(props.height),
83
+ borderRadius: radius,
84
+ }
85
+ })
86
+
87
+ function onLoad(ev: any) {
88
+ status.value = 'loaded'
89
+ emit('load', ev)
90
+ }
91
+
92
+ function onError(ev: any) {
93
+ status.value = 'error'
94
+ emit('error', ev)
95
+ }
96
+
97
+ function onClick(ev: any) {
98
+ emit('click', ev)
99
+ }
100
+
101
+ watch(
102
+ () => props.src,
103
+ (val) => {
104
+ status.value = val ? 'loading' : 'error'
105
+ }
106
+ )
107
+ </script>
108
+
109
+ <style lang="scss" scoped>
110
+ @import '../../scss/variables';
111
+ @import '../../scss/mixins';
112
+
113
+ .dd-image {
114
+ position: relative;
115
+ display: inline-block;
116
+ overflow: hidden;
117
+ background: $dd-neutral-800; // var(--dd-bg-inset) 占位底色
118
+ vertical-align: middle;
119
+
120
+ &--round {
121
+ border-radius: 50% !important;
122
+ }
123
+ }
124
+
125
+ .dd-image__img {
126
+ display: block;
127
+ width: 100%;
128
+ height: 100%;
129
+ }
130
+
131
+ .dd-image__mask {
132
+ position: absolute;
133
+ inset: 0;
134
+ @include dd-flex-center;
135
+ background: $dd-neutral-800;
136
+ }
137
+
138
+ .dd-image__spinner {
139
+ width: 48rpx;
140
+ height: 48rpx;
141
+ border: 4rpx solid $dd-border-default;
142
+ border-top-color: $dd-primary-500;
143
+ border-radius: 50%;
144
+ animation: dd-image-spin 0.8s linear infinite;
145
+ }
146
+
147
+ .dd-image__error-icon {
148
+ font-size: 48rpx; // 24px
149
+ color: $dd-text-tertiary;
150
+ line-height: 1;
151
+ }
152
+
153
+ @keyframes dd-image-spin {
154
+ to {
155
+ transform: rotate(360deg);
156
+ }
157
+ }
158
+ </style>
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <view
3
+ class="dd-input"
4
+ :class="{
5
+ 'dd-input--focus': isFocused,
6
+ 'dd-input--error': error,
7
+ 'dd-input--disabled': disabled,
8
+ }"
9
+ >
10
+ <slot name="prefix"></slot>
11
+ <input
12
+ class="dd-input__inner"
13
+ :type="inputType"
14
+ :password="isPassword"
15
+ :value="modelValue"
16
+ :placeholder="placeholder"
17
+ placeholder-style="color:#9E9E9E"
18
+ :disabled="disabled"
19
+ :maxlength="maxlength"
20
+ :confirm-type="confirmType"
21
+ @input="onInput"
22
+ @focus="onFocus"
23
+ @blur="onBlur"
24
+ @confirm="onConfirm"
25
+ />
26
+ <view class="dd-input__suffix" v-if="showClear || showToggle || $slots.suffix">
27
+ <view v-if="showClear" class="dd-input__clear" @click="onClear"><dd-icon name="cross" /></view>
28
+ <dd-icon v-if="showToggle" :name="visible ? 'eye' : 'closed-eye'" class="dd-input__toggle" @click="toggleVisible" />
29
+ <slot name="suffix"></slot>
30
+ </view>
31
+ </view>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { computed, ref } from 'vue'
36
+ import DdIcon from '../dd-icon/dd-icon.vue'
37
+
38
+ interface Props {
39
+ modelValue?: string | number
40
+ type?: 'text' | 'password' | 'search' | 'number'
41
+ placeholder?: string
42
+ disabled?: boolean
43
+ clearable?: boolean
44
+ showPassword?: boolean
45
+ error?: boolean
46
+ maxlength?: number
47
+ }
48
+
49
+ const props = withDefaults(defineProps<Props>(), {
50
+ modelValue: '',
51
+ type: 'text',
52
+ placeholder: '',
53
+ disabled: false,
54
+ clearable: false,
55
+ showPassword: false,
56
+ error: false,
57
+ maxlength: -1,
58
+ })
59
+
60
+ const emit = defineEmits<{
61
+ (e: 'update:modelValue', val: string): void
62
+ (e: 'focus', val: Event): void
63
+ (e: 'blur', val: Event): void
64
+ (e: 'clear'): void
65
+ (e: 'confirm', val: string): void
66
+ (e: 'search', val: string): void
67
+ }>()
68
+
69
+ const isFocused = ref(false)
70
+ const visible = ref(false)
71
+
72
+ // ponytail: search/number 在小程序降级处理;password 用 uni 原生 password 布尔属性跨端
73
+ const inputType = computed<'text' | 'number'>(() => {
74
+ if (props.type === 'password' || props.type === 'search') return 'text'
75
+ return 'number'
76
+ })
77
+ const isPassword = computed(() => props.type === 'password' && !visible.value)
78
+ const confirmType = computed(() => (props.type === 'search' ? 'search' : 'done'))
79
+ const showClear = computed(() => props.clearable && !!props.modelValue && !props.disabled)
80
+ const showToggle = computed(() => props.type === 'password' && props.showPassword)
81
+
82
+ function onInput(e: any) {
83
+ const v = e?.detail?.value ?? e?.target?.value ?? ''
84
+ emit('update:modelValue', v)
85
+ }
86
+ function onFocus(e: Event) {
87
+ isFocused.value = true
88
+ emit('focus', e)
89
+ }
90
+ function onBlur(e: Event) {
91
+ isFocused.value = false
92
+ emit('blur', e)
93
+ }
94
+ function onClear() {
95
+ emit('update:modelValue', '')
96
+ emit('clear')
97
+ }
98
+ function onConfirm(e: any) {
99
+ const v = e?.detail?.value ?? e?.target?.value ?? ''
100
+ emit('confirm', v)
101
+ if (props.type === 'search') emit('search', v)
102
+ }
103
+ function toggleVisible() {
104
+ visible.value = !visible.value
105
+ }
106
+ </script>
107
+
108
+ <style lang="scss" scoped>
109
+ @import '../../scss/variables';
110
+ @import '../../scss/mixins';
111
+
112
+ .dd-input {
113
+ display: flex;
114
+ align-items: center;
115
+ gap: 16rpx;
116
+ height: 88rpx;
117
+ padding: 0 32rpx;
118
+ background: $dd-neutral-800;
119
+ border: 1px solid $dd-neutral-700;
120
+ border-radius: $dd-radius-md;
121
+ box-sizing: border-box;
122
+ @include dd-transition(border-color 0.2s, box-shadow 0.2s);
123
+
124
+ &__inner {
125
+ flex: 1;
126
+ min-width: 0;
127
+ height: 100%;
128
+ font-size: 28rpx;
129
+ color: $dd-neutral-100;
130
+ background: transparent;
131
+ border: none;
132
+ }
133
+
134
+ &__suffix {
135
+ display: flex;
136
+ align-items: center;
137
+ gap: 16rpx;
138
+ }
139
+
140
+ &__clear {
141
+ @include dd-flex-center;
142
+ width: 36rpx;
143
+ height: 36rpx;
144
+ border-radius: 50%;
145
+ background: $dd-neutral-700;
146
+ color: $dd-neutral-300;
147
+ font-size: 24rpx;
148
+ line-height: 1;
149
+ }
150
+
151
+ &__toggle {
152
+ font-size: 32rpx;
153
+ opacity: 0.7;
154
+ line-height: 1;
155
+ }
156
+
157
+ &--focus {
158
+ border-color: $dd-primary-500;
159
+ box-shadow: $dd-shadow-glow-gold-lg;
160
+ }
161
+
162
+ &--error {
163
+ border-color: $dd-error-500;
164
+ box-shadow: $dd-shadow-glow-error-md;
165
+ }
166
+
167
+ &--disabled {
168
+ opacity: 0.5;
169
+ @include dd-no-touch;
170
+ }
171
+ }
172
+ </style>
@@ -0,0 +1,152 @@
1
+ <template>
2
+ <view
3
+ class="dd-list-cell"
4
+ :class="{ 'dd-list-cell--link': isLink || arrow, 'dd-list-cell--borderless': borderless }"
5
+ :hover-class="isLink || arrow ? 'dd-list-cell--active' : ''"
6
+ :hover-stay-time="100"
7
+ @click="onClick"
8
+ >
9
+ <view v-if="$slots.icon || icon" class="dd-list-cell__icon">
10
+ <slot name="icon">
11
+ <view class="dd-list-cell__icon-text">
12
+ <dd-icon v-if="icon" :name="icon" />
13
+ </view>
14
+ </slot>
15
+ </view>
16
+ <view class="dd-list-cell__content">
17
+ <text class="dd-list-cell__title">{{ title }}</text>
18
+ <text v-if="subtitle || $slots.subtitle" class="dd-list-cell__subtitle">
19
+ <slot name="subtitle">{{ subtitle }}</slot>
20
+ </text>
21
+ </view>
22
+ <view class="dd-list-cell__right">
23
+ <slot name="value">
24
+ <text v-if="value" class="dd-list-cell__value">{{ value }}</text>
25
+ </slot>
26
+ <slot name="right-icon"></slot>
27
+ <view v-if="isLink || arrow" class="dd-list-cell__arrow"><dd-icon name="arrow" /></view>
28
+ </view>
29
+ </view>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import { useSlots } from 'vue'
34
+ import DdIcon from '../dd-icon/dd-icon.vue'
35
+
36
+ interface Props {
37
+ title?: string
38
+ subtitle?: string
39
+ value?: string
40
+ icon?: string
41
+ arrow?: boolean
42
+ isLink?: boolean
43
+ borderless?: boolean
44
+ }
45
+
46
+ const props = withDefaults(defineProps<Props>(), {
47
+ title: '',
48
+ subtitle: '',
49
+ value: '',
50
+ icon: '',
51
+ arrow: false,
52
+ isLink: false,
53
+ borderless: false,
54
+ })
55
+
56
+ const emit = defineEmits<{ (e: 'click', val: Event): void }>()
57
+
58
+ const slots = useSlots()
59
+ void slots
60
+
61
+ function onClick(e: Event) {
62
+ emit('click', e)
63
+ }
64
+ </script>
65
+
66
+ <style lang="scss" scoped>
67
+ @import '../../scss/variables';
68
+ @import '../../scss/mixins';
69
+
70
+ .dd-list-cell {
71
+ display: flex;
72
+ flex-direction: row;
73
+ align-items: center;
74
+ min-height: 96rpx;
75
+ padding: 0 $dd-space-4;
76
+ box-sizing: border-box;
77
+ background: $dd-bg-elevated;
78
+ @include dd-hairline-bottom($dd-border-default);
79
+
80
+ &__icon {
81
+ flex-shrink: 0;
82
+ width: 56rpx;
83
+ height: 56rpx;
84
+ margin-right: $dd-space-3;
85
+ display: flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ }
89
+
90
+ &__icon-text {
91
+ font-size: 40rpx;
92
+ line-height: 1;
93
+ color: $dd-primary-400;
94
+ }
95
+
96
+ &__content {
97
+ flex: 1;
98
+ min-width: 0;
99
+ display: flex;
100
+ flex-direction: column;
101
+ gap: 4rpx;
102
+ }
103
+
104
+ &__title {
105
+ font-size: $dd-font-size-body;
106
+ font-weight: 500;
107
+ color: $dd-text-primary;
108
+ @include dd-ellipsis(1);
109
+ }
110
+
111
+ &__subtitle {
112
+ font-size: $dd-font-size-caption;
113
+ color: $dd-text-tertiary;
114
+ @include dd-ellipsis(1);
115
+ }
116
+
117
+ &__right {
118
+ flex-shrink: 0;
119
+ display: flex;
120
+ align-items: center;
121
+ gap: $dd-space-1;
122
+ margin-left: $dd-space-2;
123
+ }
124
+
125
+ &__value {
126
+ font-size: $dd-font-size-body;
127
+ font-weight: 500;
128
+ color: $dd-primary-400;
129
+ }
130
+
131
+ &__arrow {
132
+ font-size: 36rpx;
133
+ color: $dd-text-tertiary;
134
+ line-height: 1;
135
+ margin-left: $dd-space-1;
136
+ }
137
+
138
+ &--borderless {
139
+ &::after {
140
+ display: none;
141
+ }
142
+ }
143
+
144
+ &--link {
145
+ cursor: pointer;
146
+ }
147
+
148
+ &--active {
149
+ background: $dd-surface-container-highest;
150
+ }
151
+ }
152
+ </style>
@@ -0,0 +1,187 @@
1
+ <template>
2
+ <view v-if="fullscreen" class="dd-loading dd-loading--fullscreen" :style="{ zIndex: 9999 }">
3
+ <view class="dd-loading__mask"></view>
4
+ <view class="dd-loading__box">
5
+ <view class="dd-loading__core" :class="[`dd-loading__core--${type}`, `dd-loading__core--${size}`]" :style="coreStyle">
6
+ <template v-if="type === 'spinner'">
7
+ <view class="dd-loading__spinner"></view>
8
+ </template>
9
+ <template v-else-if="type === 'dots'">
10
+ <view class="dd-loading__dot" v-for="i in 3" :key="i" :style="{ animationDelay: `${(3 - i) * 0.16}s` }"></view>
11
+ </template>
12
+ <template v-else>
13
+ <view class="dd-loading__ring" v-for="i in 2" :key="i" :style="{ animationDelay: `${(i - 1) * 0.9}s` }"></view>
14
+ <view class="dd-loading__pulse-core"></view>
15
+ </template>
16
+ </view>
17
+ <text v-if="text" class="dd-loading__text">{{ text }}</text>
18
+ </view>
19
+ </view>
20
+ <view v-else class="dd-loading" :class="[`dd-loading--inline`, `dd-loading--${type}`, `dd-loading--${size}`]">
21
+ <view class="dd-loading__core" :class="[`dd-loading__core--${type}`, `dd-loading__core--${size}`]" :style="coreStyle">
22
+ <template v-if="type === 'spinner'">
23
+ <view class="dd-loading__spinner"></view>
24
+ </template>
25
+ <template v-else-if="type === 'dots'">
26
+ <view class="dd-loading__dot" v-for="i in 3" :key="i" :style="{ animationDelay: `${(3 - i) * 0.16}s` }"></view>
27
+ </template>
28
+ <template v-else>
29
+ <view class="dd-loading__ring" v-for="i in 2" :key="i" :style="{ animationDelay: `${(i - 1) * 0.9}s` }"></view>
30
+ <view class="dd-loading__pulse-core"></view>
31
+ </template>
32
+ </view>
33
+ <text v-if="text" class="dd-loading__text">{{ text }}</text>
34
+ </view>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ import { computed } from 'vue'
39
+
40
+ interface Props {
41
+ type?: 'spinner' | 'dots' | 'pulse'
42
+ size?: 'sm' | 'md' | 'lg'
43
+ fullscreen?: boolean
44
+ text?: string
45
+ color?: string
46
+ }
47
+
48
+ const props = withDefaults(defineProps<Props>(), {
49
+ type: 'spinner',
50
+ size: 'md',
51
+ fullscreen: false,
52
+ text: '',
53
+ color: '',
54
+ })
55
+
56
+ const coreStyle = computed(() => (props.color ? { '--dd-loading-color': props.color } : {}))
57
+ </script>
58
+
59
+ <style lang="scss" scoped>
60
+ @import '../../scss/variables';
61
+ @import '../../scss/mixins';
62
+
63
+ .dd-loading {
64
+ --dd-loading-color: #{$dd-primary-400};
65
+ display: inline-flex;
66
+ flex-direction: column;
67
+ align-items: center;
68
+ gap: $dd-space-2;
69
+
70
+ &--inline {
71
+ display: inline-flex;
72
+ }
73
+
74
+ &--fullscreen {
75
+ position: fixed;
76
+ inset: 0;
77
+ @include dd-flex-center;
78
+ }
79
+
80
+ &__mask {
81
+ position: absolute;
82
+ inset: 0;
83
+ background: rgba(0, 0, 0, 0.6);
84
+ /* #ifdef H5 */
85
+ backdrop-filter: blur(8rpx);
86
+ -webkit-backdrop-filter: blur(8rpx);
87
+ /* #endif */
88
+ }
89
+
90
+ &__box {
91
+ position: relative;
92
+ z-index: 1;
93
+ display: flex;
94
+ flex-direction: column;
95
+ align-items: center;
96
+ gap: $dd-space-3;
97
+ }
98
+
99
+ &__core {
100
+ display: inline-flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+ position: relative;
104
+
105
+ &--spinner {
106
+ &.dd-loading__core--sm { width: 40rpx; height: 40rpx; }
107
+ &.dd-loading__core--md { width: 64rpx; height: 64rpx; }
108
+ &.dd-loading__core--lg { width: 96rpx; height: 96rpx; }
109
+ }
110
+ &--dots {
111
+ &.dd-loading__core--sm .dd-loading__dot { width: 12rpx; height: 12rpx; }
112
+ &.dd-loading__core--md .dd-loading__dot { width: 20rpx; height: 20rpx; }
113
+ &.dd-loading__core--lg .dd-loading__dot { width: 28rpx; height: 28rpx; }
114
+ }
115
+ &--pulse {
116
+ &.dd-loading__core--sm { width: 40rpx; height: 40rpx; }
117
+ &.dd-loading__core--md { width: 64rpx; height: 64rpx; }
118
+ &.dd-loading__core--lg { width: 96rpx; height: 96rpx; }
119
+ &.dd-loading__core--sm .dd-loading__pulse-core { width: 16rpx; height: 16rpx; }
120
+ &.dd-loading__core--md .dd-loading__pulse-core { width: 24rpx; height: 24rpx; }
121
+ &.dd-loading__core--lg .dd-loading__pulse-core { width: 36rpx; height: 36rpx; }
122
+ }
123
+ }
124
+
125
+ &__spinner {
126
+ width: 100%;
127
+ height: 100%;
128
+ border-radius: 50%;
129
+ border-color: rgba(245, 166, 35, 0.2);
130
+ border-style: solid;
131
+ border-width: 4rpx;
132
+ border-top-color: var(--dd-loading-color);
133
+ border-right-color: var(--dd-loading-color);
134
+ box-shadow: 0 0 12rpx rgba(245, 166, 35, 0.5);
135
+ animation: dd-loading-spin 0.8s linear infinite;
136
+ }
137
+ .dd-loading__core--sm .dd-loading__spinner { border-width: 4rpx; }
138
+ .dd-loading__core--md .dd-loading__spinner { border-width: 6rpx; }
139
+ .dd-loading__core--lg .dd-loading__spinner { border-width: 8rpx; }
140
+
141
+ &__dot {
142
+ width: 20rpx;
143
+ height: 20rpx;
144
+ margin: 0 6rpx;
145
+ border-radius: 50%;
146
+ background: var(--dd-loading-color);
147
+ box-shadow: 0 0 16rpx rgba(245, 166, 35, 0.6);
148
+ animation: dd-loading-bounce 1.2s ease-in-out infinite;
149
+ }
150
+
151
+ &__ring {
152
+ position: absolute;
153
+ inset: 0;
154
+ border-radius: 50%;
155
+ border: 2rpx solid var(--dd-loading-color);
156
+ opacity: 0;
157
+ transform: scale(0.3);
158
+ animation: dd-loading-pulse 1.8s ease-out infinite;
159
+ }
160
+
161
+ &__pulse-core {
162
+ width: 24rpx;
163
+ height: 24rpx;
164
+ border-radius: 50%;
165
+ background: var(--dd-loading-color);
166
+ box-shadow: $dd-shadow-glow-gold-lg;
167
+ }
168
+
169
+ &__text {
170
+ color: $dd-text-secondary;
171
+ font-size: $dd-font-size-body;
172
+ line-height: 1.4;
173
+ }
174
+ }
175
+
176
+ @keyframes dd-loading-spin {
177
+ to { transform: rotate(360deg); }
178
+ }
179
+ @keyframes dd-loading-bounce {
180
+ 0%, 80%, 100% { transform: scale(0.6); opacity: 0.6; }
181
+ 40% { transform: scale(1); opacity: 1; }
182
+ }
183
+ @keyframes dd-loading-pulse {
184
+ 0% { transform: scale(0.3); opacity: 1; }
185
+ 100% { transform: scale(2.5); opacity: 0; }
186
+ }
187
+ </style>