@rpg-engine/long-bow 0.8.228 → 0.8.230

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.8.228",
3
+ "version": "0.8.230",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { FaTimes } from 'react-icons/fa';
2
3
  import styled from 'styled-components';
3
4
  import { RPGUIContainer, RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
4
5
  import { NPCDialogText } from './NPCDialogText';
@@ -65,9 +66,8 @@ export const NPCDialog: React.FC<INPCDialogProps> = ({
65
66
  ) : (
66
67
  <>
67
68
  <Container>
68
- <CloseIcon onPointerDown={onClose}>X</CloseIcon>
69
69
  <TextContainer
70
- flex={type === NPCDialogType.TextAndThumbnail ? '70%' : '100%'}
70
+ flex={type === NPCDialogType.TextAndThumbnail ? '1' : '100%'}
71
71
  >
72
72
  <NPCDialogText
73
73
  type={type}
@@ -82,6 +82,9 @@ export const NPCDialog: React.FC<INPCDialogProps> = ({
82
82
  </TextContainer>
83
83
  {type === NPCDialogType.TextAndThumbnail && (
84
84
  <ThumbnailContainer>
85
+ <CloseButton onPointerDown={onClose}>
86
+ <FaTimes size={12} />
87
+ </CloseButton>
85
88
  <NPCThumbnail src={imagePath || aliceDefaultThumbnail} />
86
89
  </ThumbnailContainer>
87
90
  )}
@@ -96,20 +99,41 @@ const Container = styled.div`
96
99
  display: flex;
97
100
  width: 100%;
98
101
  height: 100%;
99
-
100
102
  box-sizing: border-box;
101
- justify-content: center;
102
- align-items: flex-start;
103
+ justify-content: flex-start;
104
+ align-items: center;
103
105
  position: relative;
106
+ padding: 1rem 1rem 0.5rem 1rem;
104
107
  `;
105
108
 
106
- const CloseIcon = styled.p`
109
+ const CloseButton = styled.button`
107
110
  position: absolute;
108
- top: -1.5rem;
109
- right: -0.4rem;
111
+ top: -12px;
112
+ right: -12px;
113
+ width: 26px;
114
+ height: 26px;
115
+ background: #000;
116
+ border: 2px solid #fff;
117
+ border-radius: 2px;
118
+ color: #fff;
119
+ display: flex;
120
+ justify-content: center;
121
+ align-items: center;
110
122
  cursor: pointer;
111
- color: white;
112
- font-size: 1.1rem !important;
123
+ z-index: 10;
124
+ padding: 0;
125
+ box-shadow: 0 2px 0px rgba(0, 0, 0, 1);
126
+ transition: all 0.1s;
127
+
128
+ &:hover {
129
+ background: #333;
130
+ border-color: #eee;
131
+ }
132
+
133
+ &:active {
134
+ transform: translateY(2px);
135
+ box-shadow: 0 0 0px rgba(0, 0, 0, 1);
136
+ }
113
137
  `;
114
138
 
115
139
  interface ITextContainerProps {
@@ -117,18 +141,34 @@ interface ITextContainerProps {
117
141
  }
118
142
 
119
143
  const TextContainer = styled.div<ITextContainerProps>`
120
- flex: ${({ flex }) => flex} 0 0;
121
- width: 355px;
144
+ flex: ${({ flex }) => flex};
145
+ height: 100%;
146
+ padding-right: 1.5rem;
147
+ display: flex;
148
+ flex-direction: column;
122
149
  `;
123
150
 
124
151
  const ThumbnailContainer = styled.div`
125
- flex: 30% 0 0;
152
+ flex-shrink: 0;
153
+ position: relative;
154
+ width: 116px;
155
+ height: 116px;
156
+ /* Layered pixel-art borders: inner black -> gold -> outer black */
157
+ border: 4px solid #1a100c;
158
+ border-radius: 2px;
159
+ box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.4), 0 0 0 2px #d4a373, 0 0 0 4px #000;
160
+ background: #3e2723;
126
161
  display: flex;
127
- justify-content: flex-end;
162
+ justify-content: center;
163
+ align-items: center;
164
+ margin-right: 8px;
165
+ padding: 2px;
128
166
  `;
129
167
 
130
168
  const NPCThumbnail = styled.img`
131
169
  image-rendering: pixelated;
132
- height: 128px;
133
- width: 128px;
170
+ height: 100%;
171
+ width: 100%;
172
+ object-fit: contain;
173
+ border-radius: 1px;
134
174
  `;
@@ -118,14 +118,24 @@ export const NPCDialogText: React.FC<IProps> = ({
118
118
  );
119
119
  };
120
120
 
121
- const Container = styled.div``;
121
+ const Container = styled.div`
122
+ width: 100%;
123
+ height: 100%;
124
+ display: flex;
125
+ flex-direction: column;
126
+ position: relative;
127
+ padding-bottom: 24px;
128
+ `;
122
129
 
123
130
  const TextContainer = styled.p`
124
- font-size: 0.7rem !important;
131
+ font-size: 0.8rem !important;
132
+ line-height: 1.6;
125
133
  color: white;
126
134
  text-shadow: 1px 1px 0px #000000;
127
135
  letter-spacing: 1.2px;
128
136
  word-break: normal;
137
+ margin: 0;
138
+ flex: 1;
129
139
  `;
130
140
 
131
141
  interface IPressSpaceIndicatorProps {
@@ -134,8 +144,11 @@ interface IPressSpaceIndicatorProps {
134
144
 
135
145
  const PressSpaceIndicator = styled.img<IPressSpaceIndicatorProps>`
136
146
  position: absolute;
137
- right: ${({ right }) => right};
138
- bottom: 1rem;
139
- height: 20.7px;
147
+ left: 0;
148
+ bottom: -0.5rem;
149
+ height: 20px;
150
+ object-fit: contain;
151
+ object-position: left;
140
152
  image-rendering: -webkit-optimize-contrast;
153
+ cursor: pointer;
141
154
  `;
@@ -0,0 +1,123 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ export interface IRadioSelectCardProps {
5
+ label: string;
6
+ description?: string;
7
+ badge?: string;
8
+ icon?: React.ReactNode;
9
+ active?: boolean;
10
+ onClick: () => void;
11
+ 'data-testid'?: string;
12
+ }
13
+
14
+ /**
15
+ * Dark RPG-themed radio-style card.
16
+ * Matches the aesthetic of MarketplaceSettingsPanel OptionCard.
17
+ * Uses !important overrides to survive RPGUI global CSS.
18
+ */
19
+ export const RadioSelectCard: React.FC<IRadioSelectCardProps> = ({
20
+ label,
21
+ description,
22
+ badge,
23
+ icon,
24
+ active = false,
25
+ onClick,
26
+ 'data-testid': testId
27
+ }) => {
28
+ return (
29
+ <Card $active={active} onClick={onClick} data-testid={testId} type="button">
30
+ {icon && <IconWrap>{icon}</IconWrap>}
31
+ <Body>
32
+ <Label $active={active}>{label}</Label>
33
+ {description && <Description>{description}</Description>}
34
+ </Body>
35
+ {badge && <Badge $active={active}>{badge}</Badge>}
36
+ </Card>
37
+ );
38
+ };
39
+
40
+ // ─── Styled Components ────────────────────────────────────────────────────────
41
+
42
+ const Card = styled.button<{ $active: boolean }>`
43
+ display: flex !important;
44
+ flex-direction: row !important;
45
+ align-items: center !important;
46
+ gap: 0.6rem !important;
47
+ width: 100% !important;
48
+ padding: 0.75rem 0.9rem !important;
49
+ background: ${({ $active }) =>
50
+ $active ? 'rgba(245, 158, 11, 0.12)' : 'rgba(0, 0, 0, 0.25)'} !important;
51
+ border: 2px solid
52
+ ${({ $active }) =>
53
+ $active ? '#f59e0b' : 'rgba(255, 255, 255, 0.08)'} !important;
54
+ border-radius: 6px !important;
55
+ cursor: pointer !important;
56
+ text-align: left !important;
57
+ transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease !important;
58
+ font-family: inherit !important;
59
+ box-sizing: border-box !important;
60
+
61
+ &:hover {
62
+ border-color: ${({ $active }) =>
63
+ $active ? '#f59e0b' : 'rgba(255, 255, 255, 0.3)'} !important;
64
+ background: ${({ $active }) =>
65
+ $active ? 'rgba(245, 158, 11, 0.18)' : 'rgba(255, 255, 255, 0.04)'} !important;
66
+ box-shadow: ${({ $active }) =>
67
+ $active
68
+ ? '0 0 14px rgba(245, 158, 11, 0.3)'
69
+ : '0 0 8px rgba(255, 255, 255, 0.05)'} !important;
70
+ }
71
+
72
+ &:active {
73
+ background: rgba(0, 0, 0, 0.5) !important;
74
+ }
75
+ `;
76
+
77
+ const IconWrap = styled.span`
78
+ font-size: 1.4rem !important;
79
+ line-height: 1 !important;
80
+ flex-shrink: 0 !important;
81
+ `;
82
+
83
+ const Body = styled.div`
84
+ flex: 1 !important;
85
+ min-width: 0 !important;
86
+ display: flex !important;
87
+ flex-direction: column !important;
88
+ gap: 0.2rem !important;
89
+ `;
90
+
91
+ const Label = styled.span<{ $active: boolean }>`
92
+ font-size: 0.72rem !important;
93
+ font-weight: bold !important;
94
+ color: ${({ $active }) =>
95
+ $active ? '#f59e0b' : 'rgba(255, 255, 255, 0.9)'} !important;
96
+ text-transform: uppercase !important;
97
+ letter-spacing: 0.8px !important;
98
+ line-height: 1.2 !important;
99
+ `;
100
+
101
+ const Description = styled.span`
102
+ font-size: 0.62rem !important;
103
+ color: rgba(255, 255, 255, 0.45) !important;
104
+ line-height: 1.4 !important;
105
+ `;
106
+
107
+ const Badge = styled.span<{ $active: boolean }>`
108
+ flex-shrink: 0 !important;
109
+ padding: 0.15rem 0.45rem !important;
110
+ background: ${({ $active }) =>
111
+ $active ? 'rgba(245, 158, 11, 0.2)' : 'rgba(255, 255, 255, 0.06)'} !important;
112
+ border: 1px solid
113
+ ${({ $active }) =>
114
+ $active ? 'rgba(245, 158, 11, 0.5)' : 'rgba(255, 255, 255, 0.12)'} !important;
115
+ border-radius: 20px !important;
116
+ font-size: 0.55rem !important;
117
+ font-weight: bold !important;
118
+ letter-spacing: 0.5px !important;
119
+ text-transform: uppercase !important;
120
+ color: ${({ $active }) =>
121
+ $active ? '#f59e0b' : 'rgba(255, 255, 255, 0.45)'} !important;
122
+ white-space: nowrap !important;
123
+ `;
@@ -41,9 +41,11 @@ export const DynamicText: React.FC<IProps> = ({ text, onFinish, onStart }) => {
41
41
  };
42
42
 
43
43
  const TextContainer = styled.p`
44
- font-size: 0.7rem !important;
44
+ font-size: 0.8rem !important;
45
+ line-height: 1.6;
45
46
  color: white;
46
47
  text-shadow: 1px 1px 0px #000000;
47
48
  letter-spacing: 1.2px;
48
49
  word-break: normal;
50
+ margin: 0;
49
51
  `;
package/src/index.tsx CHANGED
@@ -63,6 +63,7 @@ export * from './components/QuantitySelector/QuantitySelectorModal';
63
63
  export * from './components/Quests/QuestInfo/QuestInfo';
64
64
  export * from './components/Quests/QuestList';
65
65
  export * from './components/RadioButton';
66
+ export * from './components/RadioSelectCard/RadioSelectCard';
66
67
  export * from './components/RangeSlider';
67
68
  export * from './components/RPGUI/RPGUIContainer';
68
69
  export * from './components/RPGUI/RPGUIRoot';